github.com/prebid/prebid-server/v2@v2.18.0/adapters/between/between.go (about) 1 package between 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strconv" 9 "text/template" 10 11 "github.com/prebid/openrtb/v20/openrtb2" 12 "github.com/prebid/prebid-server/v2/adapters" 13 "github.com/prebid/prebid-server/v2/config" 14 "github.com/prebid/prebid-server/v2/errortypes" 15 "github.com/prebid/prebid-server/v2/macros" 16 "github.com/prebid/prebid-server/v2/openrtb_ext" 17 ) 18 19 type BetweenAdapter struct { 20 EndpointTemplate *template.Template 21 } 22 23 func (a *BetweenAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 24 var errors []error 25 if len(request.Imp) == 0 { 26 return nil, []error{&errortypes.BadInput{ 27 Message: fmt.Sprintf("No valid Imps in Bid Request"), 28 }} 29 } 30 ext, errors := preprocess(request) 31 if len(errors) > 0 { 32 return nil, errors 33 } 34 endpoint, err := a.buildEndpointURL(ext) 35 if err != nil { 36 return nil, []error{&errortypes.BadInput{ 37 Message: fmt.Sprintf("Failed to build endpoint URL: %s", err), 38 }} 39 } 40 data, err := json.Marshal(request) 41 if err != nil { 42 return nil, []error{&errortypes.BadInput{ 43 Message: fmt.Sprintf("Error in packaging request to JSON"), 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, "X-Forwarded-For", request.Device.IP) 52 addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language) 53 if request.Device.DNT != nil { 54 addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT))) 55 } 56 } 57 if request.Site != nil { 58 addHeaderIfNonEmpty(headers, "Referer", request.Site.Page) 59 } 60 61 return []*adapters.RequestData{{ 62 Method: "POST", 63 Uri: endpoint, 64 Body: data, 65 Headers: headers, 66 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 67 }}, errors 68 } 69 70 func unpackImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBetween, error) { 71 var bidderExt adapters.ExtImpBidder 72 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 73 return nil, &errortypes.BadInput{ 74 Message: fmt.Sprintf("ignoring imp id=%s, invalid BidderExt", imp.ID), 75 } 76 } 77 78 var betweenExt openrtb_ext.ExtImpBetween 79 if err := json.Unmarshal(bidderExt.Bidder, &betweenExt); err != nil { 80 return nil, &errortypes.BadInput{ 81 Message: fmt.Sprintf("ignoring imp id=%s, invalid ImpExt", imp.ID), 82 } 83 } 84 85 return &betweenExt, nil 86 } 87 88 func (a *BetweenAdapter) buildEndpointURL(e *openrtb_ext.ExtImpBetween) (string, error) { 89 missingRequiredParameterMessage := "required BetweenSSP parameter \"%s\" is missing" 90 if e.Host == "" { 91 return "", &errortypes.BadInput{ 92 Message: fmt.Sprintf(missingRequiredParameterMessage, "host"), 93 } 94 } 95 if e.PublisherID == "" { 96 return "", &errortypes.BadInput{ 97 Message: fmt.Sprintf(missingRequiredParameterMessage, "publisher_id"), 98 } 99 } 100 return macros.ResolveMacros(a.EndpointTemplate, macros.EndpointTemplateParams{Host: e.Host, PublisherID: e.PublisherID}) 101 } 102 103 func buildImpBanner(imp *openrtb2.Imp) error { 104 if imp.Banner == nil { 105 return &errortypes.BadInput{ 106 Message: fmt.Sprintf("Request needs to include a Banner object"), 107 } 108 } 109 banner := *imp.Banner 110 if banner.W == nil && banner.H == nil { 111 if len(banner.Format) == 0 { 112 return &errortypes.BadInput{ 113 Message: fmt.Sprintf("Need at least one size to build request"), 114 } 115 } 116 format := banner.Format[0] 117 banner.Format = banner.Format[1:] 118 banner.W = &format.W 119 banner.H = &format.H 120 imp.Banner = &banner 121 } 122 123 return nil 124 } 125 126 // Add Between required properties to Imp object 127 func addImpProps(imp *openrtb2.Imp, secure *int8, betweenExt *openrtb_ext.ExtImpBetween) { 128 imp.Secure = secure 129 } 130 131 // Adding header fields to request header 132 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 133 if len(headerValue) > 0 { 134 headers.Add(headerName, headerValue) 135 } 136 } 137 138 // Handle request errors and formatting to be sent to Between 139 func preprocess(request *openrtb2.BidRequest) (*openrtb_ext.ExtImpBetween, []error) { 140 errors := make([]error, 0, len(request.Imp)) 141 resImps := make([]openrtb2.Imp, 0, len(request.Imp)) 142 secure := int8(0) 143 144 if request.Site != nil && request.Site.Page != "" { 145 pageURL, err := url.Parse(request.Site.Page) 146 if err == nil && pageURL.Scheme == "https" { 147 secure = int8(1) 148 } 149 } 150 151 var betweenExt *openrtb_ext.ExtImpBetween 152 for _, imp := range request.Imp { 153 var err error 154 betweenExt, err = unpackImpExt(&imp) 155 if err != nil { 156 errors = append(errors, err) 157 continue 158 } 159 160 addImpProps(&imp, &secure, betweenExt) 161 162 if err := buildImpBanner(&imp); err != nil { 163 errors = append(errors, err) 164 continue 165 } 166 resImps = append(resImps, imp) 167 } 168 request.Imp = resImps 169 170 return betweenExt, errors 171 } 172 173 func (a *BetweenAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 174 175 if response.StatusCode == http.StatusNoContent { 176 // no bid response 177 return nil, nil 178 } 179 180 if response.StatusCode != http.StatusOK { 181 return nil, []error{&errortypes.BadServerResponse{ 182 Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", response.StatusCode), 183 }} 184 } 185 var bidResp openrtb2.BidResponse 186 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 187 return nil, []error{&errortypes.BadServerResponse{ 188 Message: fmt.Sprintf("Unable to unpackage bid response. Error %s", err.Error()), 189 }} 190 } 191 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 192 for _, sb := range bidResp.SeatBid { 193 for i := range sb.Bid { 194 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 195 Bid: &sb.Bid[i], 196 BidType: openrtb_ext.BidTypeBanner, 197 }) 198 } 199 } 200 201 return bidResponse, nil 202 } 203 204 // Builder builds a new instance of the Between adapter for the given bidder with the given config. 205 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 206 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 207 if err != nil { 208 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 209 } 210 211 bidder := BetweenAdapter{ 212 EndpointTemplate: template, 213 } 214 return &bidder, nil 215 }