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