github.com/prebid/prebid-server/v2@v2.18.0/adapters/sovrn/sovrn.go (about) 1 package sovrn 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strconv" 9 "strings" 10 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 "github.com/prebid/openrtb/v20/openrtb2" 17 ) 18 19 type SovrnAdapter struct { 20 URI string 21 } 22 23 type sovrnImpExt struct { 24 Bidder openrtb_ext.ExtImpSovrn `json:"bidder"` 25 AdUnitCode string `json:"adunitcode,omitempty"` 26 } 27 28 func (s *SovrnAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 29 headers := http.Header{} 30 headers.Add("Content-Type", "application/json") 31 if request.Device != nil { 32 addHeaderIfNonEmpty(headers, "User-Agent", request.Device.UA) 33 addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IP) 34 addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language) 35 if request.Device.DNT != nil { 36 addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT))) 37 } 38 } 39 40 if request.User != nil { 41 userID := strings.TrimSpace(request.User.BuyerUID) 42 if len(userID) > 0 { 43 headers.Add("Cookie", fmt.Sprintf("%s=%s", "ljt_reader", userID)) 44 } 45 } 46 47 errs := make([]error, 0, len(request.Imp)) 48 var err error 49 validImps := make([]openrtb2.Imp, 0, len(request.Imp)) 50 51 for _, imp := range request.Imp { 52 var bidderExt adapters.ExtImpBidder 53 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 54 errs = append(errs, &errortypes.BadInput{ 55 Message: err.Error(), 56 }) 57 continue 58 } 59 60 var sovrnExt openrtb_ext.ExtImpSovrn 61 if err := json.Unmarshal(bidderExt.Bidder, &sovrnExt); err != nil { 62 errs = append(errs, &errortypes.BadInput{ 63 Message: err.Error(), 64 }) 65 continue 66 } 67 68 tagId := getTagId(sovrnExt) 69 if tagId == "" { 70 errs = append(errs, &errortypes.BadInput{ 71 Message: "Missing required parameter 'tagid'", 72 }) 73 continue 74 } 75 76 imp.TagID = tagId 77 78 if imp.BidFloor == 0 && sovrnExt.BidFloor > 0 { 79 imp.BidFloor = sovrnExt.BidFloor 80 } 81 82 var impExtBuffer []byte 83 impExtBuffer, err = json.Marshal(&sovrnImpExt{ 84 Bidder: sovrnExt, 85 AdUnitCode: sovrnExt.AdUnitCode, 86 }) 87 if err != nil { 88 errs = append(errs, &errortypes.BadInput{ 89 Message: err.Error(), 90 }) 91 continue 92 } 93 94 imp.Ext = impExtBuffer 95 96 // Validate video params if appropriate 97 video := imp.Video 98 if video != nil { 99 if video.MIMEs == nil || 100 video.MaxDuration == 0 || 101 video.Protocols == nil { 102 errs = append(errs, &errortypes.BadInput{ 103 Message: "Missing required video parameter", 104 }) 105 continue 106 } 107 } 108 109 validImps = append(validImps, imp) 110 } 111 112 if len(validImps) == 0 { 113 return nil, errs 114 } 115 116 request.Imp = validImps 117 118 reqJSON, err := json.Marshal(request) 119 if err != nil { 120 errs = append(errs, err) 121 return nil, errs 122 } 123 124 return []*adapters.RequestData{{ 125 Method: "POST", 126 Uri: s.URI, 127 Body: reqJSON, 128 Headers: headers, 129 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 130 }}, errs 131 } 132 133 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 134 if len(headerValue) > 0 { 135 headers.Add(headerName, headerValue) 136 } 137 } 138 139 func (s *SovrnAdapter) MakeBids(request *openrtb2.BidRequest, bidderRequest *adapters.RequestData, bidderResponse *adapters.ResponseData) (*adapters.BidderResponse, []error) { 140 if bidderResponse.StatusCode == http.StatusNoContent { 141 return nil, nil 142 } 143 144 if bidderResponse.StatusCode == http.StatusBadRequest { 145 return nil, []error{&errortypes.BadInput{ 146 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", bidderResponse.StatusCode), 147 }} 148 } 149 150 if bidderResponse.StatusCode != http.StatusOK { 151 return nil, []error{&errortypes.BadServerResponse{ 152 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", bidderResponse.StatusCode), 153 }} 154 } 155 156 var bidResponse openrtb2.BidResponse 157 if err := json.Unmarshal(bidderResponse.Body, &bidResponse); err != nil { 158 return nil, []error{&errortypes.BadServerResponse{ 159 Message: err.Error(), 160 }} 161 } 162 163 response := adapters.NewBidderResponseWithBidsCapacity(5) 164 errs := make([]error, 0) 165 166 for _, sb := range bidResponse.SeatBid { 167 for _, bid := range sb.Bid { 168 adm, err := url.QueryUnescape(bid.AdM) 169 if err == nil { 170 bid.AdM = adm 171 172 bidType := openrtb_ext.BidTypeBanner 173 174 impIdx, impIdErr := getImpIdx(bid.ImpID, request) 175 if impIdErr != nil { 176 errs = append(errs, impIdErr) 177 continue 178 } else if request.Imp[impIdx].Video != nil { 179 bidType = openrtb_ext.BidTypeVideo 180 } 181 182 response.Bids = append(response.Bids, &adapters.TypedBid{ 183 Bid: &bid, 184 BidType: bidType, 185 }) 186 } 187 } 188 } 189 190 return response, errs 191 } 192 193 func getTagId(sovrnExt openrtb_ext.ExtImpSovrn) string { 194 if len(sovrnExt.Tagid) > 0 { 195 return sovrnExt.Tagid 196 } else { 197 return sovrnExt.TagId 198 } 199 } 200 201 func getImpIdx(impId string, request *openrtb2.BidRequest) (int, error) { 202 for idx, imp := range request.Imp { 203 if imp.ID == impId { 204 return idx, nil 205 } 206 } 207 208 return -1, &errortypes.BadInput{ 209 Message: fmt.Sprintf("Imp ID %s in bid didn't match with any imp in the original request", impId), 210 } 211 } 212 213 // Builder builds a new instance of the Sovrn adapter for the given bidder with the given config. 214 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 215 bidder := &SovrnAdapter{ 216 URI: config.Endpoint, 217 } 218 return bidder, nil 219 }