github.com/prebid/prebid-server@v0.275.0/adapters/connectad/connectad.go (about) 1 package connectad 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strconv" 9 10 "github.com/prebid/openrtb/v19/openrtb2" 11 "github.com/prebid/prebid-server/adapters" 12 "github.com/prebid/prebid-server/config" 13 "github.com/prebid/prebid-server/errortypes" 14 "github.com/prebid/prebid-server/openrtb_ext" 15 ) 16 17 type ConnectAdAdapter struct { 18 endpoint string 19 } 20 21 // Builder builds a new instance of the ConnectAd adapter for the given bidder with the given config. 22 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 23 bidder := &ConnectAdAdapter{ 24 endpoint: config.Endpoint, 25 } 26 return bidder, nil 27 } 28 29 func (a *ConnectAdAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 30 31 var errs []error 32 33 if errs := preprocess(request); len(errs) > 0 { 34 return nil, append(errs, &errortypes.BadInput{ 35 Message: fmt.Sprintf("Error in preprocess of Imp"), 36 }) 37 } 38 39 data, err := json.Marshal(request) 40 if err != nil { 41 return nil, []error{&errortypes.BadInput{ 42 Message: fmt.Sprintf("Error in packaging request to JSON"), 43 }} 44 } 45 46 headers := http.Header{} 47 headers.Add("Content-Type", "application/json;charset=utf-8") 48 headers.Add("Accept", "application/json") 49 if request.Device != nil { 50 addHeaderIfNonEmpty(headers, "User-Agent", request.Device.UA) 51 addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language) 52 if request.Device.IP != "" { 53 addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IP) 54 } else if request.Device.IPv6 != "" { 55 addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IPv6) 56 } 57 if request.Device.DNT != nil { 58 addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT))) 59 } else { 60 addHeaderIfNonEmpty(headers, "DNT", "0") 61 } 62 } 63 64 return []*adapters.RequestData{{ 65 Method: "POST", 66 Uri: a.endpoint, 67 Body: data, 68 Headers: headers, 69 }}, errs 70 } 71 72 func (a *ConnectAdAdapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { 73 74 if httpRes.StatusCode == http.StatusNoContent { 75 return nil, nil 76 } 77 78 if httpRes.StatusCode != http.StatusOK { 79 return nil, []error{&errortypes.BadServerResponse{ 80 Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", httpRes.StatusCode), 81 }} 82 } 83 84 var bidResp openrtb2.BidResponse 85 86 if err := json.Unmarshal(httpRes.Body, &bidResp); err != nil { 87 return nil, []error{&errortypes.BadServerResponse{ 88 Message: fmt.Sprintf("Unable to unpackage bid response. Error: %s", err.Error()), 89 }} 90 } 91 92 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid)) 93 94 for _, sb := range bidResp.SeatBid { 95 for i := range sb.Bid { 96 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 97 Bid: &sb.Bid[i], 98 BidType: "banner", 99 }) 100 } 101 } 102 103 return bidResponse, nil 104 } 105 106 func preprocess(request *openrtb2.BidRequest) []error { 107 impsCount := len(request.Imp) 108 errors := make([]error, 0, impsCount) 109 resImps := make([]openrtb2.Imp, 0, impsCount) 110 secure := int8(0) 111 112 if request.Site != nil && request.Site.Page != "" { 113 pageURL, err := url.Parse(request.Site.Page) 114 if err == nil && pageURL.Scheme == "https" { 115 secure = int8(1) 116 } 117 } 118 119 for _, imp := range request.Imp { 120 cadExt, err := unpackImpExt(&imp) 121 if err != nil { 122 errors = append(errors, err) 123 continue 124 } 125 126 addImpInfo(&imp, &secure, cadExt) 127 128 if err := buildImpBanner(&imp); err != nil { 129 errors = append(errors, err) 130 continue 131 } 132 resImps = append(resImps, imp) 133 } 134 135 request.Imp = resImps 136 137 return errors 138 } 139 140 func addImpInfo(imp *openrtb2.Imp, secure *int8, cadExt *openrtb_ext.ExtImpConnectAd) { 141 imp.TagID = strconv.Itoa(cadExt.SiteID) 142 imp.Secure = secure 143 144 if cadExt.Bidfloor != 0 { 145 imp.BidFloor = cadExt.Bidfloor 146 imp.BidFloorCur = "USD" 147 } 148 149 return 150 } 151 152 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 153 if len(headerValue) > 0 { 154 headers.Add(headerName, headerValue) 155 } 156 } 157 158 func unpackImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpConnectAd, error) { 159 var bidderExt adapters.ExtImpBidder 160 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 161 return nil, &errortypes.BadInput{ 162 Message: fmt.Sprintf("Impression id=%s has an Error: %s", imp.ID, err.Error()), 163 } 164 } 165 166 var cadExt openrtb_ext.ExtImpConnectAd 167 if err := json.Unmarshal(bidderExt.Bidder, &cadExt); err != nil { 168 return nil, &errortypes.BadInput{ 169 Message: fmt.Sprintf("Impression id=%s, has invalid Ext", imp.ID), 170 } 171 } 172 173 if cadExt.SiteID == 0 { 174 return nil, &errortypes.BadInput{ 175 Message: fmt.Sprintf("Impression id=%s, has no siteId present", imp.ID), 176 } 177 } 178 179 return &cadExt, nil 180 } 181 182 func buildImpBanner(imp *openrtb2.Imp) error { 183 imp.Ext = nil 184 185 if imp.Banner == nil { 186 return &errortypes.BadInput{ 187 Message: fmt.Sprintf("We need a Banner Object in the request"), 188 } 189 } 190 191 if imp.Banner.W == nil && imp.Banner.H == nil { 192 bannerCopy := *imp.Banner 193 banner := &bannerCopy 194 195 if len(banner.Format) == 0 { 196 return &errortypes.BadInput{ 197 Message: fmt.Sprintf("At least one size is required"), 198 } 199 } 200 format := banner.Format[0] 201 banner.Format = banner.Format[1:] 202 banner.W = &format.W 203 banner.H = &format.H 204 imp.Banner = banner 205 } 206 207 return nil 208 }