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  }