You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.7 KiB
145 lines
3.7 KiB
package clientserver |
|
|
|
import ( |
|
"encoding/json" |
|
"errors" |
|
"fmt" |
|
"io/ioutil" |
|
"log" |
|
"net/http" |
|
customtypes "sample-choose-ad/cmd/custom_types" |
|
req_types "sample-choose-ad/cmd/requests_types" |
|
"strconv" |
|
"sync" |
|
) |
|
|
|
const PARTNER_ENDPOINT = "bid_request" |
|
|
|
// 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" { |
|
w.WriteHeader(http.StatusBadRequest) |
|
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 { |
|
log.Println("Unmarshaling problem", string(body)) |
|
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) |
|
} |
|
// UserAgent validation |
|
if len(inpReqBody.Context.UserAgent) == 0 { |
|
return inpReqBody, throwHTTPError("EMPTY_FIELD", 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) |
|
|
|
wg := new(sync.WaitGroup) |
|
responsesCh := make(chan []req_types.RespImp, len(partners)) |
|
var respImps []req_types.RespImp |
|
// Send requests to partners, collecting responses in `responses` channel |
|
for _, p := range partners { |
|
wg.Add(1) |
|
url := fmt.Sprintf("http://%v:%v/%v", p.Ip, p.Port, PARTNER_ENDPOINT) |
|
go makeRequest(url, &p_body, responsesCh, wg) |
|
} |
|
wg.Wait() |
|
close(responsesCh) |
|
|
|
for r := range responsesCh { |
|
respImps = append(respImps, r...) |
|
} |
|
//We have no identical pairs `id` and `price` |
|
partnersRespones := make(map[uint]req_types.RespImp) |
|
for _, resp := range respImps { |
|
if _, exist := partnersRespones[resp.Id]; !exist { |
|
partnersRespones[resp.Id] = resp |
|
continue |
|
} |
|
|
|
// Replase with new Imp, if last saved price smaller |
|
// Using type switch and addition checks, 'cause `Price` is interface |
|
var oldPrice float64 |
|
switch partnersRespones[resp.Id].Price.(type) { |
|
case string: |
|
oldPrice, _ = strconv.ParseFloat(partnersRespones[resp.Id].Price.(string), 64) |
|
case float64: |
|
oldPrice = partnersRespones[resp.Id].Price.(float64) |
|
} |
|
|
|
var newPrice float64 |
|
switch resp.Price.(type) { |
|
case string: |
|
newPrice, _ = strconv.ParseFloat(resp.Price.(string), 64) |
|
case float64: |
|
newPrice = resp.Price.(float64) |
|
} |
|
|
|
if oldPrice < newPrice { |
|
partnersRespones[resp.Id] = resp |
|
} |
|
} |
|
|
|
var bestOptions []req_types.RespImp |
|
|
|
// tile.Id == RespImp.Id |
|
// for each tile peak best price |
|
for _, tile := range incReq.Tiles { |
|
if val, exist := partnersRespones[tile.Id]; exist { |
|
bestOptions = append(bestOptions, val) |
|
} |
|
|
|
} |
|
|
|
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) |
|
} |
|
}
|
|
|