Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ix adapter to make multiple bid requests for multiple slots and sizes #725

Merged
merged 4 commits into from
Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 152 additions & 70 deletions adapters/ix/ix.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"io/ioutil"
"net/http"

"github.com/prebid/prebid-server/openrtb_ext"

"github.com/prebid/prebid-server/pbs"

"golang.org/x/net/context/ctxhttp"
Expand All @@ -17,14 +19,17 @@ import (
"github.com/prebid/prebid-server/errortypes"
)

// maximum number of bid requests
const requestLimit = 20

type IxAdapter struct {
http *adapters.HTTPAdapter
URI string
}

// used for cookies and such
// Name is used for cookies and such
func (a *IxAdapter) Name() string {
return "ix"
return string(openrtb_ext.BidderIx)
}

func (a *IxAdapter) SkipNoCookies() bool {
Expand All @@ -35,7 +40,23 @@ type indexParams struct {
SiteID string `json:"siteId"`
}

type ixBidResult struct {
Request *callOneObject
StatusCode int
ResponseBody string
Bid *pbs.PBSBid
Error error
}

type callOneObject struct {
requestJSON bytes.Buffer
width uint64
height uint64
}

func (a *IxAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.PBSBidder) (pbs.PBSBidSlice, error) {
var prioritizedRequests, requests []callOneObject

if req.App != nil {
dbemiller marked this conversation as resolved.
Show resolved Hide resolved
return nil, &errortypes.BadInput{
Message: "Index doesn't support apps",
Expand All @@ -48,123 +69,184 @@ func (a *IxAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.P
return nil, err
}

indexReqImp := indexReq.Imp
for i, unit := range bidder.AdUnits {
var params indexParams
err := json.Unmarshal(unit.Params, &params)
if err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("unmarshal params '%s' failed: %v", unit.Params, err),
}

// Fixes some segfaults. Since this is legacy code, I'm not looking into it too deeply
if len(indexReqImp) <= i {
break
}
if params.SiteID == "" {
return nil, &errortypes.BadInput{
Message: "Missing siteId param",

for sizeIndex, format := range unit.Sizes {
var params indexParams
err := json.Unmarshal(unit.Params, &params)
if err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("unmarshal params '%s' failed: %v", unit.Params, err),
}
}
if params.SiteID == "" {
return nil, &errortypes.BadInput{
Message: "Missing siteId param",
}
}

// Only grab this ad unit
// Not supporting multi-media-type adunit yet
thisImp := indexReqImp[i]

thisImp.TagID = unit.Code
thisImp.Banner.Format = []openrtb.Format{format}
thisImp.Banner.W = &format.W
thisImp.Banner.H = &format.H
indexReq.Imp = []openrtb.Imp{thisImp}
// Index spec says "adunit path representing ad server inventory" but we don't have this
// ext is DFP div ID and KV pairs if avail
//indexReq.Imp[i].Ext = json.RawMessage("{}")

indexReq.Site.Publisher = &openrtb.Publisher{ID: fmt.Sprintf("%s", params.SiteID)}
dbemiller marked this conversation as resolved.
Show resolved Hide resolved

// spec also asks for publisher id if set
// ext object on request for prefetch
j, _ := json.Marshal(indexReq)

request := callOneObject{requestJSON: *bytes.NewBuffer(j), width: format.W, height: format.H}

// prioritize slots over sizes
if sizeIndex == 0 {
prioritizedRequests = append(prioritizedRequests, request)
} else {
requests = append(requests, request)
}
}
}

// Fixes some segfaults. Since this is legacy code, I'm not looking into it too deeply
if len(indexReq.Imp) <= i {
break
// cap the number of requests to requestLimit
requests = append(prioritizedRequests, requests...)
if len(requests) > requestLimit {
requests = requests[:requestLimit]
}

if len(requests) == 0 {
return nil, &errortypes.BadInput{
Message: "Invalid ad unit/imp",
}
}

indexReq.Imp[i].TagID = unit.Code
// Index spec says "adunit path representing ad server inventory" but we don't have this
// ext is DFP div ID and KV pairs if avail
//indexReq.Imp[i].Ext = json.RawMessage("{}")
siteCopy := *indexReq.Site
siteCopy.Publisher = &openrtb.Publisher{ID: fmt.Sprintf("%s", params.SiteID)}
indexReq.Site = &siteCopy
ch := make(chan ixBidResult)
for _, request := range requests {
go func(bidder *pbs.PBSBidder, request callOneObject) {
result, err := a.callOne(ctx, request.requestJSON)
result.Request = &request
result.Error = err
if result.Bid != nil {
result.Bid.BidderCode = bidder.BidderCode
result.Bid.BidID = bidder.LookupBidID(result.Bid.AdUnitCode)
result.Bid.Width = request.width
result.Bid.Height = request.height

if result.Bid.BidID == "" {
result.Error = &errortypes.BadServerResponse{
Message: fmt.Sprintf("Unknown ad unit code '%s'", result.Bid.AdUnitCode),
}
result.Bid = nil
}
}
ch <- result
}(bidder, request)
}
// spec also asks for publisher id if set
// ext object on request for prefetch

j, _ := json.Marshal(indexReq)
bids := make(pbs.PBSBidSlice, 0)
for i := 0; i < len(requests); i++ {
result := <-ch
if result.Bid != nil && result.Bid.Price != 0 {
bids = append(bids, result.Bid)
}

debug := &pbs.BidderDebug{
RequestURI: a.URI,
if req.IsDebug {
debug := &pbs.BidderDebug{
RequestURI: a.URI,
RequestBody: result.Request.requestJSON.String(),
StatusCode: result.StatusCode,
ResponseBody: result.ResponseBody,
}
bidder.Debug = append(bidder.Debug, debug)
}
if result.Error != nil {
err = result.Error
}
}

if req.IsDebug {
debug.RequestBody = string(j)
bidder.Debug = append(bidder.Debug, debug)
if len(bids) == 0 {
return nil, err
}
return bids, nil
}

httpReq, err := http.NewRequest("POST", a.URI, bytes.NewBuffer(j))
func (a *IxAdapter) callOne(ctx context.Context, reqJSON bytes.Buffer) (ixBidResult, error) {
var result ixBidResult

httpReq, _ := http.NewRequest("POST", a.URI, &reqJSON)
httpReq.Header.Add("Content-Type", "application/json;charset=utf-8")
httpReq.Header.Add("Accept", "application/json")

ixResp, err := ctxhttp.Do(ctx, a.http.Client, httpReq)
if err != nil {
return nil, err
return result, err
}

debug.StatusCode = ixResp.StatusCode
result.StatusCode = ixResp.StatusCode

if ixResp.StatusCode == http.StatusNoContent {
return nil, nil
return result, nil
}

if ixResp.StatusCode == http.StatusBadRequest {
return nil, &errortypes.BadInput{
return result, &errortypes.BadInput{
Message: fmt.Sprintf("HTTP status: %d", ixResp.StatusCode),
}
}

if ixResp.StatusCode != http.StatusOK {
return nil, &errortypes.BadServerResponse{
return result, &errortypes.BadServerResponse{
Message: fmt.Sprintf("HTTP status: %d", ixResp.StatusCode),
}
}

defer ixResp.Body.Close()
body, err := ioutil.ReadAll(ixResp.Body)
if err != nil {
return nil, err
}

if req.IsDebug {
debug.ResponseBody = string(body)
return result, err
}
result.ResponseBody = string(body)

var bidResp openrtb.BidResponse
err = json.Unmarshal(body, &bidResp)
if err != nil {
return nil, &errortypes.BadServerResponse{
return result, &errortypes.BadServerResponse{
Message: fmt.Sprintf("Error parsing response: %v", err),
}
}

bids := make(pbs.PBSBidSlice, 0)

for _, sb := range bidResp.SeatBid {
for _, bid := range sb.Bid {
bidID := bidder.LookupBidID(bid.ImpID)
if bidID == "" {
return nil, &errortypes.BadServerResponse{
Message: fmt.Sprintf("Unknown ad unit code '%s'", bid.ImpID),
}
}
if len(bidResp.SeatBid) == 0 {
return result, nil
}
if len(bidResp.SeatBid[0].Bid) == 0 {
return result, nil
}
bid := bidResp.SeatBid[0].Bid[0]

for _, adunit := range bidder.AdUnits {
if adunit.BidID == bidID {
pbid := pbs.PBSBid{
BidID: bidID,
AdUnitCode: adunit.Code,
BidderCode: bidder.BidderCode,
Price: bid.Price,
Adm: bid.AdM,
Creative_id: bid.CrID,
Width: bid.W,
Height: bid.H,
DealId: bid.DealID,
CreativeMediaType: "banner",
}
bids = append(bids, &pbid)
}
}
}
result.Bid = &pbs.PBSBid{
AdUnitCode: bid.ImpID,
Price: bid.Price,
Adm: bid.AdM,
Creative_id: bid.CrID,
Width: bid.W,
Height: bid.H,
DealId: bid.DealID,
CreativeMediaType: string(openrtb_ext.BidTypeBanner),
}
return bids, nil
return result, nil
}

func NewIxAdapter(config *adapters.HTTPAdapterConfig, uri string) *IxAdapter {
Expand Down
Loading