Browse Source

Bugfix

main
Дмитрий 2 years ago
parent
commit
c15de011d8
  1. 4
      Makefile
  2. 97
      cmd/client_server/client.go
  3. 79
      cmd/client_server/handlers.go
  4. 5
      cmd/client_server/server.go
  5. 14
      cmd/requests_types/response_type.go
  6. 10
      internal/json/valid_response.json
  7. 5
      internal/moc_server.go

4
Makefile

@ -1,7 +1,6 @@
# port for main server # port for main server
port := 5053 port := 5053
moc_server_address := 127.0.0.1:5059 moc_server_address := 127.0.0.1:5059
bold := \033[1m bold := \033[1m
normal := \033[0m normal := \033[0m
good := \033[1m\033[0;32m good := \033[1m\033[0;32m
@ -44,6 +43,9 @@ test-port-endpoint:
build: build:
go build -o bin/simple-choose-ad cmd/main.go go build -o bin/simple-choose-ad cmd/main.go
build-and-push:
@GOOS=linux GOARCH=amd64 go build -o build/ssp cmd/main.go && rsync -ah build/ssp ubuntu:~/ssp-testbed-clone/builds
start-moc-server: start-moc-server:
@echo "[!] Starting up moc-server on $(moc_server_address) ..." @echo "[!] Starting up moc-server on $(moc_server_address) ..."
@go run internal/moc_server.go -l $(moc_server_address) & @go run internal/moc_server.go -l $(moc_server_address) &

97
cmd/client_server/client.go

@ -2,45 +2,66 @@ package clientserver
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"math" "math"
"net/http" "net/http"
customtypes "sample-choose-ad/cmd/custom_types"
req_types "sample-choose-ad/cmd/requests_types" req_types "sample-choose-ad/cmd/requests_types"
"sort" "sync"
"time" "time"
) )
func sendRequest(url string, body *[]byte) (req_types.SuccesResponse, error) { func makeRequest(url string, body *[]byte, response chan<- []req_types.RespImp, wg *sync.WaitGroup) {
defer wg.Done()
var pResp req_types.SuccesResponse var pResp req_types.SuccesResponse
c := &http.Client{ c := &http.Client{}
Timeout: 200 * time.Millisecond,
}
resp, err := c.Post(url, "application/json", bytes.NewReader(*body)) ctx, cls := context.WithTimeout(context.Background(), time.Duration(200*time.Millisecond))
defer cls()
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(*body))
resp, err := c.Do(req)
// not responding or timeout
if err != nil { if err != nil {
eText := fmt.Sprintf("Error: partner %v not responding", url) log.Println("Error when making request", err)
return pResp, errors.New(eText) return
} }
defer resp.Body.Close() defer resp.Body.Close()
// maybe say smth to client?
if resp.StatusCode == 204 { if resp.StatusCode == 204 {
return pResp, errors.New("No content") log.Println("No content")
return
} }
b, _ := ioutil.ReadAll(resp.Body) b, _ := ioutil.ReadAll(resp.Body)
log.Println(string(b))
if json.Unmarshal(b, &pResp) != nil { if err := json.Unmarshal(b, &pResp); err != nil {
log.Println(err) log.Println("Error: response unmarshalling", err)
return
} }
return pResp, nil // try to convert prices to float
// for _, imp := range pResp.Imp {
// // log.Printf("%v : %T", imp.PriceStr, imp.PriceStr)
// // imp.Price = imp.PriceStr.(float64)
// // switch imp.PriceStr.(type) {
// // case float64:
// // imp.Price = imp.PriceStr.(float64)
// // case string:
// // imp.Price, err = strconv.ParseFloat(imp.PriceStr.(string), 64)
// // if err != nil {
// // log.Println("Pasring price error, ", err)
// // }
// // }
// }
response <- pResp.Imp
} }
// Create requset body based in incoming reqest `ir` and return // Create requset body based in incoming reqest `ir` and return
@ -64,47 +85,3 @@ func constructPartnersRequestBody(ir *req_types.IncomingRequest) []byte {
t, _ := json.Marshal(outReqBody) t, _ := json.Marshal(outReqBody)
return t return t
} }
// map[imp.id]map[imp.id.price]
type PartnersResponses map[uint]map[float64]req_types.RespImp
// Make request for each partner and returns
func makePartnersRequests(partners []customtypes.PartnersAddress, ir *req_types.IncomingRequest) {
p_body := constructPartnersRequestBody(ir)
// Two data structures:
// partnersRespones for getting price with O(1) complexity
// []prices as slice of actual prices
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
}
// append 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)
}
}

79
cmd/client_server/handlers.go

@ -9,7 +9,7 @@ import (
"net/http" "net/http"
customtypes "sample-choose-ad/cmd/custom_types" customtypes "sample-choose-ad/cmd/custom_types"
req_types "sample-choose-ad/cmd/requests_types" req_types "sample-choose-ad/cmd/requests_types"
"sort" "sync"
) )
const PARTNER_ENDPOINT = "bid_request" const PARTNER_ENDPOINT = "bid_request"
@ -48,7 +48,10 @@ func parseAndCheckIncomingRequest(w http.ResponseWriter, r *http.Request) (req_t
if wrongIPAddresFormat(inpReqBody.Context.Ip) { if wrongIPAddresFormat(inpReqBody.Context.Ip) {
return inpReqBody, throwHTTPError("WRONG_SCHEMA", 400, &w) 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 return inpReqBody, err
} }
@ -66,55 +69,65 @@ func handleRequest(partners []customtypes.PartnersAddress) http.HandlerFunc {
p_body := constructPartnersRequestBody(&incReq) p_body := constructPartnersRequestBody(&incReq)
// Two data structures: wg := new(sync.WaitGroup)
// partnersRespones for getting price with O(1) complexity responsesCh := make(chan []req_types.RespImp, len(partners))
// []prices as slice of actual prices var respImps []req_types.RespImp
partnersRespones := make(PartnersResponses) // Send requests to partners, collecting responses in `responses` channel
prices := make(map[uint][]float64)
for _, p := range partners { for _, p := range partners {
wg.Add(1)
url := fmt.Sprintf("http://%v:%v/%v", p.Ip, p.Port, PARTNER_ENDPOINT) url := fmt.Sprintf("http://%v:%v/%v", p.Ip, p.Port, PARTNER_ENDPOINT)
go makeRequest(url, &p_body, responsesCh, wg)
}
wg.Wait()
close(responsesCh)
re, err := sendRequest(url, &p_body) for r := range responsesCh {
respImps = append(respImps, r...)
if err != nil { }
log.Println(err) // У нас нет одинаковых пар цена и ид
partnersRespones := make(map[uint]req_types.RespImp)
for _, resp := range respImps {
if _, exist := partnersRespones[resp.Id]; !exist {
partnersRespones[resp.Id] = resp
continue continue
} }
// append 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)
}
// Replase with new Imp, if last saved price smaller
// Using type assertion, 'cause `Price` is interface
if partnersRespones[resp.Id].Price.(float64) < resp.Price.(float64) {
partnersRespones[resp.Id] = resp
}
} }
if len(partnersRespones) == 0 { // { drop this
log.Println("Error: no responses from partners.") log.Println("respImps")
return for _, i := range respImps {
log.Printf("%v : %v", i.Id, i.Price)
} }
// Sorting prices, now biggest price at index len-1 log.Println("partnersRespones")
for _, p := range prices { for _, i := range partnersRespones {
sort.Float64s(p) log.Printf("%v : %v", i.Id, i.Price)
} }
// }
var bestOptions []req_types.RespImp var bestOptions []req_types.RespImp
// tile.Id == RespImp.Id
// for each tile peak best price // for each tile peak best price
for _, tile := range incReq.Tiles { for _, tile := range incReq.Tiles {
if len(prices[tile.Id]) == 0 { if val, exist := partnersRespones[tile.Id]; exist {
log.Println("No imp for tile ", tile.Id) bestOptions = append(bestOptions, val)
continue
} }
last := len(prices[tile.Id]) - 1
biggestPrice := prices[tile.Id][last]
bestOptions = append(bestOptions, partnersRespones[tile.Id][biggestPrice])
} }
// if len(bestOptions) == 0 {
// // log.Println("Error: no responses from partners.")
// log.Println("Error: No Content")
// w.WriteHeader(http.StatusNoContent)
// return
// }
response := req_types.SuccesResponse{ response := req_types.SuccesResponse{
Id: *incReq.Id, Id: *incReq.Id,
Imp: bestOptions, Imp: bestOptions,

5
cmd/client_server/server.go

@ -8,6 +8,8 @@ import (
"time" "time"
) )
const MAX_TIME_PER_REQUEST = time.Duration(250 * time.Millisecond)
type customHandler struct { type customHandler struct {
Parners []customtypes.PartnersAddress Parners []customtypes.PartnersAddress
// context? // context?
@ -40,8 +42,9 @@ func StartServer(port string, partners []customtypes.PartnersAddress) {
// } // }
// s.ListenAndServe() // s.ListenAndServe()
// h := http.HandleFunc("/placements/request", handleRequest(partners)) // h := http.HandleFunc("/placements/request", handleRequest(partners))
h := http.TimeoutHandler(handleRequest(partners), time.Duration(240*time.Millisecond), "{}") h := http.TimeoutHandler(handleRequest(partners), MAX_TIME_PER_REQUEST, "")
http.Handle("/placements/request", h) http.Handle("/placements/request", h)
// http.Handle("/placements/request", handleRequest(partners))
// http.HandleFunc("/placements/request", decorate(test2)) // http.HandleFunc("/placements/request", decorate(test2))
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))

14
cmd/requests_types/response_type.go

@ -1,12 +1,14 @@
package req_types package req_types
// Price is interface (but will be used as float64) 'cuz it can be quoted or unquoted
type RespImp struct { type RespImp struct {
Id uint `json:"id"` Id uint `json:"id"`
Width uint `json:"width"` Width uint `json:"width"`
Height uint `json:"height"` Height uint `json:"height"`
Title string `json:"title"` Title string `json:"title"`
Url string `json:"url"` Url string `json:"url"`
Price float64 `json:"price,string"` // Price expects as float64, but it may be quoted or unquoted
Price interface{} `json:"price"`
} }
// Response from ad partners // Response from ad partners

10
internal/json/valid_response.json

@ -2,7 +2,7 @@
"id": "123", "id": "123",
"imp": [ "imp": [
{ {
"id": 123, "id": 1234,
"width": 144, "width": 144,
"height": 122, "height": 122,
"title": "example1", "title": "example1",
@ -10,20 +10,20 @@
"price": 123.5 "price": 123.5
}, },
{ {
"id": 123, "id": 1234,
"width": 155, "width": 155,
"height": 133, "height": 133,
"title": "bestoption", "title": "bestoption",
"url": "bestoption.com", "url": "bestoption.com",
"price": 143.8 "price": "143.8"
}, },
{ {
"id": 123, "id": 1234,
"width": 155, "width": 155,
"height": 133, "height": 133,
"title": "notabestoption", "title": "notabestoption",
"url": "notabestoption.com", "url": "notabestoption.com",
"price": 100.8 "price": 110.8
} }
] ]
} }

5
internal/moc_server.go

@ -2,6 +2,7 @@ package main
import ( import (
"flag" "flag"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -20,8 +21,8 @@ func main() {
} }
http.HandleFunc("/bid_request", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/bid_request", func(w http.ResponseWriter, r *http.Request) {
// b, _ := ioutil.ReadAll(r.Body) inc, _ := ioutil.ReadAll(r.Body)
// log.Println(string(b)) log.Println(string(inc))
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
w.Write(file) w.Write(file)
}) })

Loading…
Cancel
Save