github.com/prebid/prebid-server/v2@v2.18.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/v20/openrtb2" 11 "github.com/prebid/prebid-server/v2/adapters" 12 "github.com/prebid/prebid-server/v2/config" 13 "github.com/prebid/prebid-server/v2/errortypes" 14 "github.com/prebid/prebid-server/v2/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 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 70 }}, errs 71 } 72 73 func (a *ConnectAdAdapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { 74 75 if httpRes.StatusCode == http.StatusNoContent { 76 return nil, nil 77 } 78 79 if httpRes.StatusCode != http.StatusOK { 80 return nil, []error{&errortypes.BadServerResponse{ 81 Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", httpRes.StatusCode), 82 }} 83 } 84 85 var bidResp openrtb2.BidResponse 86 87 if err := json.Unmarshal(httpRes.Body, &bidResp); err != nil { 88 return nil, []error{&errortypes.BadServerResponse{ 89 Message: fmt.Sprintf("Unable to unpackage bid response. Error: %s", err.Error()), 90 }} 91 } 92 93 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid)) 94 95 for _, sb := range bidResp.SeatBid { 96 for i := range sb.Bid { 97 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 98 Bid: &sb.Bid[i], 99 BidType: "banner", 100 }) 101 } 102 } 103 104 return bidResponse, nil 105 } 106 107 func preprocess(request *openrtb2.BidRequest) []error { 108 impsCount := len(request.Imp) 109 errors := make([]error, 0, impsCount) 110 resImps := make([]openrtb2.Imp, 0, impsCount) 111 secure := int8(0) 112 113 if request.Site != nil && request.Site.Page != "" { 114 pageURL, err := url.Parse(request.Site.Page) 115 if err == nil && pageURL.Scheme == "https" { 116 secure = int8(1) 117 } 118 } 119 120 for _, imp := range request.Imp { 121 cadExt, err := unpackImpExt(&imp) 122 if err != nil { 123 errors = append(errors, err) 124 continue 125 } 126 127 addImpInfo(&imp, &secure, cadExt) 128 129 if err := buildImpBanner(&imp); err != nil { 130 errors = append(errors, err) 131 continue 132 } 133 resImps = append(resImps, imp) 134 } 135 136 request.Imp = resImps 137 138 return errors 139 } 140 141 func addImpInfo(imp *openrtb2.Imp, secure *int8, cadExt *openrtb_ext.ExtImpConnectAd) { 142 imp.TagID = strconv.Itoa(cadExt.SiteID) 143 imp.Secure = secure 144 145 if cadExt.Bidfloor != 0 { 146 imp.BidFloor = cadExt.Bidfloor 147 imp.BidFloorCur = "USD" 148 } 149 } 150 151 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 152 if len(headerValue) > 0 { 153 headers.Add(headerName, headerValue) 154 } 155 } 156 157 func unpackImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpConnectAd, error) { 158 var bidderExt adapters.ExtImpBidder 159 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 160 return nil, &errortypes.BadInput{ 161 Message: fmt.Sprintf("Impression id=%s has an Error: %s", imp.ID, err.Error()), 162 } 163 } 164 165 var cadExt openrtb_ext.ExtImpConnectAd 166 if err := json.Unmarshal(bidderExt.Bidder, &cadExt); err != nil { 167 return nil, &errortypes.BadInput{ 168 Message: fmt.Sprintf("Impression id=%s, has invalid Ext", imp.ID), 169 } 170 } 171 172 if cadExt.SiteID == 0 { 173 return nil, &errortypes.BadInput{ 174 Message: fmt.Sprintf("Impression id=%s, has no siteId present", imp.ID), 175 } 176 } 177 178 return &cadExt, nil 179 } 180 181 func buildImpBanner(imp *openrtb2.Imp) error { 182 imp.Ext = nil 183 184 if imp.Banner == nil { 185 return &errortypes.BadInput{ 186 Message: fmt.Sprintf("We need a Banner Object in the request"), 187 } 188 } 189 190 if imp.Banner.W == nil && imp.Banner.H == nil { 191 bannerCopy := *imp.Banner 192 banner := &bannerCopy 193 194 if len(banner.Format) == 0 { 195 return &errortypes.BadInput{ 196 Message: fmt.Sprintf("At least one size is required"), 197 } 198 } 199 format := banner.Format[0] 200 banner.Format = banner.Format[1:] 201 banner.W = &format.W 202 banner.H = &format.H 203 imp.Banner = banner 204 } 205 206 return nil 207 }