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 := 5053
moc_server_address := 127.0.0.1:5059
bold := \033[1m
normal := \033[0m
good := \033[1m\033[0;32m
@ -44,6 +43,9 @@ test-port-endpoint:
build:
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:
@echo "[!] Starting up moc-server on $(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 (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"math"
"net/http"
customtypes "sample-choose-ad/cmd/custom_types"
req_types "sample-choose-ad/cmd/requests_types"
"sort"
"sync"
"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
c := &http.Client{
Timeout: 200 * time.Millisecond,
}
c := &http.Client{}
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 {
eText := fmt.Sprintf("Error: partner %v not responding", url)
return pResp, errors.New(eText)
log.Println("Error when making request", err)
return
}
defer resp.Body.Close()
// maybe say smth to client?
if resp.StatusCode == 204 {
return pResp, errors.New("No content")
log.Println("No content")
return
}
b, _ := ioutil.ReadAll(resp.Body)
if json.Unmarshal(b, &pResp) != nil {
log.Println(err)
log.Println(string(b))
if err := json.Unmarshal(b, &pResp); err != nil {
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
@ -64,47 +85,3 @@ func constructPartnersRequestBody(ir *req_types.IncomingRequest) []byte {
t, _ := json.Marshal(outReqBody)
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"
customtypes "sample-choose-ad/cmd/custom_types"
req_types "sample-choose-ad/cmd/requests_types"
"sort"
"sync"
)
const PARTNER_ENDPOINT = "bid_request"
@ -48,7 +48,10 @@ func parseAndCheckIncomingRequest(w http.ResponseWriter, r *http.Request) (req_t
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
}
@ -66,55 +69,65 @@ func handleRequest(partners []customtypes.PartnersAddress) http.HandlerFunc {
p_body := constructPartnersRequestBody(&incReq)
// Two data structures:
// partnersRespones for getting price with O(1) complexity
// []prices as slice of actual prices
partnersRespones := make(PartnersResponses)
prices := make(map[uint][]float64)
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)
re, err := sendRequest(url, &p_body)
if err != nil {
log.Println(err)
for r := range responsesCh {
respImps = append(respImps, r...)
}
// У нас нет одинаковых пар цена и ид
partnersRespones := make(map[uint]req_types.RespImp)
for _, resp := range respImps {
if _, exist := partnersRespones[resp.Id]; !exist {
partnersRespones[resp.Id] = resp
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 {
log.Println("Error: no responses from partners.")
return
// { drop this
log.Println("respImps")
for _, i := range respImps {
log.Printf("%v : %v", i.Id, i.Price)
}
// Sorting prices, now biggest price at index len-1
for _, p := range prices {
sort.Float64s(p)
log.Println("partnersRespones")
for _, i := range partnersRespones {
log.Printf("%v : %v", i.Id, i.Price)
}
// }
var bestOptions []req_types.RespImp
// tile.Id == RespImp.Id
// for each tile peak best price
for _, tile := range incReq.Tiles {
if len(prices[tile.Id]) == 0 {
log.Println("No imp for tile ", tile.Id)
continue
if val, exist := partnersRespones[tile.Id]; exist {
bestOptions = append(bestOptions, val)
}
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{
Id: *incReq.Id,
Imp: bestOptions,

5
cmd/client_server/server.go

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

14
cmd/requests_types/response_type.go

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

10
internal/json/valid_response.json

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

5
internal/moc_server.go

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

Loading…
Cancel
Save