diff --git a/Makefile b/Makefile index cae4bc0..8c8a1c6 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,19 @@ run: - go run src/main.go -p 5053 -d "127.0.0.1:5059" + go run cmd/main.go -p 5053 -d "127.0.0.1:5059" test-ip: - go run src/main.go -p 5053 -d "127.0.0.1:5059,localhost:5059" + go run cmd/main.go -p 5053 -d "127.0.0.1:5059,localhost:5059" test-port: - go run src/main.go -p 5053 -d "127.0.0.1:5059,127.0.0.1:as" + go run cmd/main.go -p 5053 -d "127.0.0.1:5059,127.0.0.1:as" test-port-max: - go run src/main.go -p 5053 -d "127.0.0.1:5059,127.0.0.1:65537" + go run cmd/main.go -p 5053 -d "127.0.0.1:5059,127.0.0.1:65537" test-port-endpoint: - go run src/main.go -p 5053 -d "127.0.0.1:9001/bid_request" + go run cmd/main.go -p 5053 -d "127.0.0.1:9001/bid_request" build: - go build -o bin/simple-choose-ad src/main.go + go build -o bin/simple-choose-ad cmd/main.go + +tests: diff --git a/src/client_server/client.go b/src/client_server/client.go deleted file mode 100644 index f1aa91f..0000000 --- a/src/client_server/client.go +++ /dev/null @@ -1,76 +0,0 @@ -package clientserver - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - req_types "sample-choose-ad/src/requests_types" - "time" -) - -func sendRequest(url string, body *io.Reader) (req_types.SuccesResponse, error) { - var pResp req_types.SuccesResponse - - c := &http.Client{ - Timeout: 200 * time.Millisecond, - } - - resp, err := c.Post(url, "application/json", *body) - - if err != nil { - log.Println(err) - eText := fmt.Sprintf("%v\n not responding", url) - return pResp, errors.New(eText) - } - defer resp.Body.Close() - - if resp.StatusCode == 204 { - return pResp, errors.New("No content") - } - - b, _ := ioutil.ReadAll(resp.Body) - - err = json.Unmarshal(b, &pResp) - if err != nil { - log.Println(err) - } - return pResp, nil -} - -/* - key string - - map[price]{Imp} - -*/ - -func sendRequest2(url string, body *io.Reader) ([]req_types.RespImp, error) { - var pResp req_types.SuccesResponse - - c := &http.Client{ - Timeout: 200 * time.Millisecond, - } - - resp, err := c.Post(url, "application/json", *body) - - if err != nil { - log.Println(err) - } - - if resp.StatusCode == 204 { - return pResp.Imp, errors.New("No content") - } - - b, _ := ioutil.ReadAll(resp.Body) - - err = json.Unmarshal(b, &pResp) - if err != nil { - log.Println(err) - } - - return pResp.Imp, nil -} diff --git a/src/client_server/handlers.go b/src/client_server/handlers.go deleted file mode 100644 index 6fb9025..0000000 --- a/src/client_server/handlers.go +++ /dev/null @@ -1,154 +0,0 @@ -package clientserver - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "math" - "net/http" - customtypes "sample-choose-ad/src/custom_types" - req_types "sample-choose-ad/src/requests_types" - "sort" -) - -const PARTNER_ENDPOINT = "bid_request" - -// Create requset body based in incoming reqest `ir` and return -// `OutgoingRequest` as bytes.Reader from marshaled JSON -func constructPartnersRequestBody(ir *req_types.IncomingRequest) io.Reader { - var outReqBody req_types.OutgoingRequest - - var imps []req_types.Imp - - // WARN: uint and float multiplication may cause problems - for _, tile := range ir.Tiles { - imps = append(imps, req_types.Imp{ - Id: tile.Id, - Minwidth: tile.Width, - Minheight: uint(math.Floor(float64(tile.Width * uint(tile.Ratio))))}) - } - - outReqBody.Id = *ir.Id - outReqBody.Imp = imps - outReqBody.Context = ir.Context - t, _ := json.Marshal(outReqBody) - return bytes.NewReader(t) -} - -// Parsing and checking incoming request. -func parseAndCheckIncomingRequest(w http.ResponseWriter, r *http.Request) (req_types.IncomingRequest, error) { - - var inpReqBody req_types.IncomingRequest - var err error - - //check request method. Only POST valid. - if r.Method == "GET" { - return inpReqBody, errors.New("Wrong request method") - } - - // Check if body in incoming request is empty - body, _ := ioutil.ReadAll(r.Body) - - if json.Unmarshal(body, &inpReqBody) != nil { - return inpReqBody, throwHTTPError("WRONG_SCHEMA", 400, &w) - } - - // Check if Id is empty - if inpReqBody.Id == nil { - return inpReqBody, throwHTTPError("EMPTY_FIELD", 400, &w) - } - - // Check if tiles is empty - if len(inpReqBody.Tiles) == 0 { - return inpReqBody, throwHTTPError("EMPTY_TILES", 400, &w) - } - - // ipv4 validation - if wrongIPAddresFormat(inpReqBody.Context.Ip) { - return inpReqBody, throwHTTPError("WRONG_SCHEMA", 400, &w) - } - - return inpReqBody, err -} - -// Request handler with wrapper (make request for each partner in `[]partners`). -func handleRequest(partners []customtypes.PartnersAddress) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - // Parse incoming request and return an error, if it's empty - // or contains wrong/empty fields - incReq, err := parseAndCheckIncomingRequest(w, r) - if err != nil { - log.Println(err) - return - } - - p_body := constructPartnersRequestBody(&incReq) - - // Two data structures: - // partnersRespones for getting price with O(1) complexity - // []prices as slice of actual prices - // var partnersRespones map[float64]req_types.RespImp - partnersRespones := make(map[uint]map[float64]req_types.RespImp) - prices := make(map[uint][]float64) - - for _, p := range partners { - url := fmt.Sprintf("http://%v:%v/%v", p.Ip, p.Port, PARTNER_ENDPOINT) - - re, err := sendRequest(url, &p_body) - - if err != nil { - log.Println(err) - continue - } - // adding only successful responses - for _, r := range re.Imp { - if partnersRespones[r.Id] == nil { - partnersRespones[r.Id] = make(map[float64]req_types.RespImp) - } - partnersRespones[r.Id][r.Price] = r - prices[r.Id] = append(prices[r.Id], r.Price) - } - - } - - if len(partnersRespones) == 0 { - log.Println("Error: no responses from partners.") - return - } - - // Sorting prices, now biggest price at index len-1 - for _, p := range prices { - sort.Float64s(p) - } - - var bestOptions []req_types.RespImp - - // for each tile peak best price - for _, tile := range incReq.Tiles { - last := len(prices[tile.Id]) - 1 - biggestPrice := prices[tile.Id][last] - _ = biggestPrice - bestOptions = append(bestOptions, partnersRespones[tile.Id][biggestPrice]) - } - - response := req_types.SuccesResponse{ - Id: *incReq.Id, - Imp: bestOptions, - } - - respJSON, err := json.Marshal(response) - - if err != nil { - log.Println(err) - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(respJSON) - } -} diff --git a/src/client_server/server.go b/src/client_server/server.go deleted file mode 100644 index 8bcdcf4..0000000 --- a/src/client_server/server.go +++ /dev/null @@ -1,17 +0,0 @@ -package clientserver - -import ( - "fmt" - "log" - "net/http" - customtypes "sample-choose-ad/src/custom_types" -) - -func StartServer(port string, partners []customtypes.PartnersAddress) { - - http.HandleFunc("/placements/request", handleRequest(partners)) - // http.HandleFunc("/placements/request", decorate(test2)) - - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) - -} diff --git a/src/client_server/validators.go b/src/client_server/validators.go deleted file mode 100644 index 2af10c5..0000000 --- a/src/client_server/validators.go +++ /dev/null @@ -1,51 +0,0 @@ -package clientserver - -import ( - "errors" - "fmt" - "log" - "net/http" - "regexp" - "strconv" - "strings" -) - -const MAX_PORT_NUM = 65535 - -// Returns false if ipv4 `correct`. -func wrongIPAddresFormat(ipv4 string) bool { - re, err := regexp.Compile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`) - if err != nil { - log.Println(err) - } - return !re.Match([]byte(ipv4)) -} - -func throwHTTPError(err_text string, code int, w *http.ResponseWriter) error { - http.Error(*w, err_text, code) - eText := fmt.Sprintf("Error: %d %v", code, err_text) - return errors.New(eText) -} - -// Wait string in format "10.10.10.10:8080", where `10.10.10.10` IPv4, -// and `8080` port. If ip or port has wrong format, returns error. -func ParsePartnersAddress(ipAndPort string) (string, int64, error) { - var err error - iap := strings.Split(ipAndPort, ":") - - ip := iap[0] - if wrongIPAddresFormat(ip) { - err = errors.New(fmt.Sprintf("Wrong ip address format in partner ip: %v", ip)) - } - - port, e := strconv.ParseInt(iap[1], 10, 32) - if e != nil { - err = errors.New(fmt.Sprintf("Wrong port format in partner ip: %v", e)) - } - - if port > MAX_PORT_NUM { - err = errors.New(fmt.Sprintf("Wrong port in partner ip: grater than %v", MAX_PORT_NUM)) - } - - return ip, port, err -} diff --git a/src/custom_types/partners_address.go b/src/custom_types/partners_address.go deleted file mode 100644 index fac959f..0000000 --- a/src/custom_types/partners_address.go +++ /dev/null @@ -1,6 +0,0 @@ -package customtypes - -type PartnersAddress struct { - Ip string - Port int64 -} diff --git a/src/main.go b/src/main.go deleted file mode 100644 index a0dbd6b..0000000 --- a/src/main.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Usage: - - sample-choose-ad [flags] - -The flags are: - - -p - Listening port - -d - Adversment partners list in format ip_p1:port,ip_p2:port2...ip_p10:port -*/ -package main - -import ( - "flag" - "log" - clientserver "sample-choose-ad/src/client_server" - customtypes "sample-choose-ad/src/custom_types" - "strings" -) - -func main() { - log.Println("Info: Starting server") - - port := flag.String("p", "", "-p 5050") - addressesList := flag.String("d", "", "-d '10.10.10.10:5050,10.10.10.20:5050'") - flag.Parse() - - if *port == "" { - log.Fatalln("Error: Port number is require!") - } - - if *addressesList == "" { - log.Fatalln("Error: Partners list is require!") - } - - // Parse first 10 ip:port pairs into `[]partners` slise - var partners []customtypes.PartnersAddress - for i, p := range strings.Split(*addressesList, ",") { - - if i == 10 { - log.Println("Warning: Partners count must be less or equal 10!") - return - } - - ip, port, err := clientserver.ParsePartnersAddress(p) - - if err != nil { - log.Fatalln(err) - } - - partners = append(partners, customtypes.PartnersAddress{ - Ip: ip, - Port: port}) - } - - clientserver.StartServer(*port, partners) -} diff --git a/src/requests_types/requests_types.go b/src/requests_types/requests_types.go deleted file mode 100644 index 4574f27..0000000 --- a/src/requests_types/requests_types.go +++ /dev/null @@ -1,31 +0,0 @@ -package req_types - -type Tile struct { - Id uint `json:"id"` - Width uint `json:"width"` - Ratio float64 `json:"ratio"` -} - -type AdContext struct { - Ip string `json:"ip"` - UserAgent string `json:"user_agent"` -} - -type IncomingRequest struct { - Id *string `json:"id"` - Tiles []Tile `json:"tiles"` - Context AdContext `json:"context"` -} - -// Based in Tile -type Imp struct { - Id uint `json:"id"` // same as related `Tile.Id` - Minwidth uint `json:"minwidth"` // `Tile.Width` - Minheight uint `json:"minheight"` // math.Floor(Tile.Width * Tile.Ratio) -} - -type OutgoingRequest struct { - Id string `json:"id"` - Imp []Imp `json:"imp"` - Context AdContext `json:"context"` -} diff --git a/src/requests_types/response_type.go b/src/requests_types/response_type.go deleted file mode 100644 index 6dabcf8..0000000 --- a/src/requests_types/response_type.go +++ /dev/null @@ -1,16 +0,0 @@ -package req_types - -type RespImp struct { - Id uint `json:"id"` - Width uint `json:"width"` - Height uint `json:"height"` - Tile string `json:"tile"` - Url string `json:"url"` - Price float64 `json:"price"` -} - -// Response from ad partners -type SuccesResponse struct { - Id string `json:"id"` - Imp []RespImp `json:"imp"` -}