github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 12 "github.com/prebid/prebid-server/adapters" 13 "github.com/prebid/prebid-server/config" 14 "github.com/prebid/prebid-server/errortypes" 15 "github.com/prebid/prebid-server/macros" 16 "github.com/prebid/prebid-server/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 errors != nil && 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 }}, errors 67 } 68 69 func unpackImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBetween, error) { 70 var bidderExt adapters.ExtImpBidder 71 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 72 return nil, &errortypes.BadInput{ 73 Message: fmt.Sprintf("ignoring imp id=%s, invalid BidderExt", imp.ID), 74 } 75 } 76 77 var betweenExt openrtb_ext.ExtImpBetween 78 if err := json.Unmarshal(bidderExt.Bidder, &betweenExt); err != nil { 79 return nil, &errortypes.BadInput{ 80 Message: fmt.Sprintf("ignoring imp id=%s, invalid ImpExt", imp.ID), 81 } 82 } 83 84 return &betweenExt, nil 85 } 86 87 func (a *BetweenAdapter) buildEndpointURL(e *openrtb_ext.ExtImpBetween) (string, error) { 88 missingRequiredParameterMessage := "required BetweenSSP parameter \"%s\" is missing" 89 if e.Host == "" { 90 return "", &errortypes.BadInput{ 91 Message: fmt.Sprintf(missingRequiredParameterMessage, "host"), 92 } 93 } 94 if e.PublisherID == "" { 95 return "", &errortypes.BadInput{ 96 Message: fmt.Sprintf(missingRequiredParameterMessage, "publisher_id"), 97 } 98 } 99 return macros.ResolveMacros(a.EndpointTemplate, macros.EndpointTemplateParams{Host: e.Host, PublisherID: e.PublisherID}) 100 } 101 102 func buildImpBanner(imp *openrtb2.Imp) error { 103 if imp.Banner == nil { 104 return &errortypes.BadInput{ 105 Message: fmt.Sprintf("Request needs to include a Banner object"), 106 } 107 } 108 banner := *imp.Banner 109 if banner.W == nil && banner.H == nil { 110 if len(banner.Format) == 0 { 111 return &errortypes.BadInput{ 112 Message: fmt.Sprintf("Need at least one size to build request"), 113 } 114 } 115 format := banner.Format[0] 116 banner.Format = banner.Format[1:] 117 banner.W = &format.W 118 banner.H = &format.H 119 imp.Banner = &banner 120 } 121 122 return nil 123 } 124 125 // Add Between required properties to Imp object 126 func addImpProps(imp *openrtb2.Imp, secure *int8, betweenExt *openrtb_ext.ExtImpBetween) { 127 imp.Secure = secure 128 } 129 130 // Adding header fields to request header 131 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 132 if len(headerValue) > 0 { 133 headers.Add(headerName, headerValue) 134 } 135 } 136 137 // Handle request errors and formatting to be sent to Between 138 func preprocess(request *openrtb2.BidRequest) (*openrtb_ext.ExtImpBetween, []error) { 139 errors := make([]error, 0, len(request.Imp)) 140 resImps := make([]openrtb2.Imp, 0, len(request.Imp)) 141 secure := int8(0) 142 143 if request.Site != nil && request.Site.Page != "" { 144 pageURL, err := url.Parse(request.Site.Page) 145 if err == nil && pageURL.Scheme == "https" { 146 secure = int8(1) 147 } 148 } 149 150 var betweenExt *openrtb_ext.ExtImpBetween 151 for _, imp := range request.Imp { 152 var err error 153 betweenExt, err = unpackImpExt(&imp) 154 if err != nil { 155 errors = append(errors, err) 156 continue 157 } 158 159 addImpProps(&imp, &secure, betweenExt) 160 161 if err := buildImpBanner(&imp); err != nil { 162 errors = append(errors, err) 163 continue 164 } 165 resImps = append(resImps, imp) 166 } 167 request.Imp = resImps 168 169 return betweenExt, errors 170 } 171 172 func (a *BetweenAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 173 174 if response.StatusCode == http.StatusNoContent { 175 // no bid response 176 return nil, nil 177 } 178 179 if response.StatusCode != http.StatusOK { 180 return nil, []error{&errortypes.BadServerResponse{ 181 Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", response.StatusCode), 182 }} 183 } 184 var bidResp openrtb2.BidResponse 185 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 186 return nil, []error{&errortypes.BadServerResponse{ 187 Message: fmt.Sprintf("Unable to unpackage bid response. Error %s", err.Error()), 188 }} 189 } 190 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 191 for _, sb := range bidResp.SeatBid { 192 for i := range sb.Bid { 193 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 194 Bid: &sb.Bid[i], 195 BidType: openrtb_ext.BidTypeBanner, 196 }) 197 } 198 } 199 200 return bidResponse, nil 201 } 202 203 // Builder builds a new instance of the Between adapter for the given bidder with the given config. 204 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 205 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 206 if err != nil { 207 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 208 } 209 210 bidder := BetweenAdapter{ 211 EndpointTemplate: template, 212 } 213 return &bidder, nil 214 }