github.com/prebid/prebid-server@v0.275.0/adapters/beachfront/beachfront.go (about)

     1  package beachfront
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/prebid/openrtb/v19/adcom1"
    12  	"github.com/prebid/openrtb/v19/openrtb2"
    13  	"github.com/prebid/prebid-server/adapters"
    14  	"github.com/prebid/prebid-server/config"
    15  	"github.com/prebid/prebid-server/errortypes"
    16  	"github.com/prebid/prebid-server/openrtb_ext"
    17  )
    18  
    19  const Seat = "beachfront"
    20  const BidCapacity = 5
    21  
    22  const defaultVideoEndpoint = "https://reachms.bfmio.com/bid.json?exchange_id"
    23  
    24  const nurlVideoEndpointSuffix = "&prebidserver"
    25  
    26  const beachfrontAdapterName = "BF_PREBID_S2S"
    27  const beachfrontAdapterVersion = "1.0.0"
    28  
    29  const minBidFloor = 0.01
    30  
    31  const defaultVideoWidth = 300
    32  const defaultVideoHeight = 250
    33  const fakeIP = "255.255.255.255"
    34  
    35  type BeachfrontAdapter struct {
    36  	bannerEndpoint string
    37  	extraInfo      ExtraInfo
    38  }
    39  
    40  type ExtraInfo struct {
    41  	VideoEndpoint string `json:"video_endpoint,omitempty"`
    42  }
    43  
    44  type beachfrontRequests struct {
    45  	Banner    beachfrontBannerRequest
    46  	NurlVideo []beachfrontVideoRequest
    47  	ADMVideo  []beachfrontVideoRequest
    48  }
    49  
    50  // ---------------------------------------------------
    51  //              Video
    52  // ---------------------------------------------------
    53  
    54  type beachfrontVideoRequest struct {
    55  	AppId             string              `json:"appId"`
    56  	VideoResponseType string              `json:"videoResponseType"`
    57  	Request           openrtb2.BidRequest `json:"request"`
    58  }
    59  
    60  // ---------------------------------------------------
    61  //
    62  //	Banner
    63  //
    64  // ---------------------------------------------------
    65  type beachfrontBannerRequest struct {
    66  	Slots          []beachfrontSlot     `json:"slots"`
    67  	Domain         string               `json:"domain"`
    68  	Page           string               `json:"page"`
    69  	Referrer       string               `json:"referrer"`
    70  	Search         string               `json:"search"`
    71  	Secure         int8                 `json:"secure"`
    72  	DeviceOs       string               `json:"deviceOs"`
    73  	DeviceModel    string               `json:"deviceModel"`
    74  	IsMobile       int8                 `json:"isMobile"`
    75  	UA             string               `json:"ua"`
    76  	Dnt            int8                 `json:"dnt"`
    77  	User           openrtb2.User        `json:"user"`
    78  	AdapterName    string               `json:"adapterName"`
    79  	AdapterVersion string               `json:"adapterVersion"`
    80  	IP             string               `json:"ip"`
    81  	RequestID      string               `json:"requestId"`
    82  	Real204        bool                 `json:"real204"`
    83  	SChain         openrtb2.SupplyChain `json:"schain,omitempty"`
    84  }
    85  
    86  type beachfrontSlot struct {
    87  	Slot     string           `json:"slot"`
    88  	Id       string           `json:"id"`
    89  	Bidfloor float64          `json:"bidfloor"`
    90  	Sizes    []beachfrontSize `json:"sizes"`
    91  }
    92  
    93  type beachfrontSize struct {
    94  	W uint64 `json:"w"`
    95  	H uint64 `json:"h"`
    96  }
    97  
    98  // ---------------------------------------------------
    99  // 				Banner response
   100  // ---------------------------------------------------
   101  
   102  type beachfrontResponseSlot struct {
   103  	CrID  string  `json:"crid"`
   104  	Price float64 `json:"price"`
   105  	W     uint64  `json:"w"`
   106  	H     uint64  `json:"h"`
   107  	Slot  string  `json:"slot"`
   108  	Adm   string  `json:"adm"`
   109  }
   110  
   111  type beachfrontVideoBidExtension struct {
   112  	Duration int `json:"duration"`
   113  }
   114  
   115  func (a *BeachfrontAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
   116  	beachfrontRequests, errs := preprocess(request, reqInfo)
   117  
   118  	headers := http.Header{}
   119  	headers.Add("Content-Type", "application/json;charset=utf-8")
   120  	headers.Add("Accept", "application/json")
   121  
   122  	if request.Device != nil {
   123  		if request.Device.UA != "" {
   124  			headers.Add("User-Agent", request.Device.UA)
   125  		}
   126  
   127  		if request.Device.Language != "" {
   128  			headers.Add("Accept-Language", request.Device.Language)
   129  		}
   130  
   131  		if request.Device.DNT != nil {
   132  			headers.Add("DNT", strconv.Itoa(int(*request.Device.DNT)))
   133  		}
   134  	}
   135  
   136  	var reqCount = len(beachfrontRequests.ADMVideo) + len(beachfrontRequests.NurlVideo)
   137  	if len(beachfrontRequests.Banner.Slots) > 0 {
   138  		reqCount++
   139  	}
   140  
   141  	var reqs = make([]*adapters.RequestData, reqCount)
   142  
   143  	var nurlBump = 0
   144  	var admBump = 0
   145  
   146  	if len(beachfrontRequests.Banner.Slots) > 0 {
   147  		bytes, err := json.Marshal(beachfrontRequests.Banner)
   148  
   149  		if err == nil {
   150  			reqs[0] = &adapters.RequestData{
   151  				Method:  "POST",
   152  				Uri:     a.bannerEndpoint,
   153  				Body:    bytes,
   154  				Headers: headers,
   155  			}
   156  
   157  			nurlBump++
   158  			admBump++
   159  		} else {
   160  			errs = append(errs, err)
   161  		}
   162  	}
   163  
   164  	if request.User != nil && request.User.BuyerUID != "" && reqCount > 0 {
   165  		headers.Add("Cookie", "__io_cid="+request.User.BuyerUID)
   166  	}
   167  
   168  	for j := 0; j < len(beachfrontRequests.ADMVideo); j++ {
   169  		bytes, err := json.Marshal(beachfrontRequests.ADMVideo[j].Request)
   170  		if err == nil {
   171  			reqs[j+nurlBump] = &adapters.RequestData{
   172  				Method:  "POST",
   173  				Uri:     a.extraInfo.VideoEndpoint + "=" + beachfrontRequests.ADMVideo[j].AppId,
   174  				Body:    bytes,
   175  				Headers: headers,
   176  			}
   177  
   178  			admBump++
   179  
   180  		} else {
   181  			errs = append(errs, err)
   182  		}
   183  	}
   184  
   185  	for j := 0; j < len(beachfrontRequests.NurlVideo); j++ {
   186  		bytes, err := json.Marshal(beachfrontRequests.NurlVideo[j].Request)
   187  
   188  		if err == nil {
   189  			bytes = append([]byte(`{"isPrebid":true,`), bytes[1:]...)
   190  			reqs[j+admBump] = &adapters.RequestData{
   191  				Method:  "POST",
   192  				Uri:     a.extraInfo.VideoEndpoint + "=" + beachfrontRequests.NurlVideo[j].AppId + nurlVideoEndpointSuffix,
   193  				Body:    bytes,
   194  				Headers: headers,
   195  			}
   196  		} else {
   197  			errs = append(errs, err)
   198  		}
   199  	}
   200  
   201  	return reqs, errs
   202  }
   203  
   204  func preprocess(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (beachfrontReqs beachfrontRequests, errs []error) {
   205  	var videoImps = make([]openrtb2.Imp, 0)
   206  	var bannerImps = make([]openrtb2.Imp, 0)
   207  
   208  	for i := 0; i < len(request.Imp); i++ {
   209  		if request.Imp[i].Banner != nil && request.Imp[i].Banner.Format != nil &&
   210  			request.Imp[i].Banner.Format[0].H != 0 && request.Imp[i].Banner.Format[0].W != 0 {
   211  			bannerImps = append(bannerImps, request.Imp[i])
   212  		}
   213  
   214  		if request.Imp[i].Video != nil {
   215  			videoImps = append(videoImps, request.Imp[i])
   216  		}
   217  	}
   218  
   219  	if len(bannerImps)+len(videoImps) == 0 {
   220  		errs = append(errs, errors.New("no valid impressions were found in the request"))
   221  		return
   222  	}
   223  
   224  	if len(bannerImps) > 0 {
   225  		request.Imp = bannerImps
   226  		beachfrontReqs.Banner, errs = getBannerRequest(request, reqInfo)
   227  	}
   228  
   229  	if len(videoImps) > 0 {
   230  		var videoErrs []error
   231  		var videoList []beachfrontVideoRequest
   232  
   233  		request.Imp = videoImps
   234  		request.Ext = nil
   235  
   236  		videoList, videoErrs = getVideoRequests(request, reqInfo)
   237  		errs = append(errs, videoErrs...)
   238  
   239  		for i := 0; i < len(videoList); i++ {
   240  			if videoList[i].VideoResponseType == "nurl" {
   241  				beachfrontReqs.NurlVideo = append(beachfrontReqs.NurlVideo, videoList[i])
   242  			}
   243  
   244  			if videoList[i].VideoResponseType == "adm" {
   245  				beachfrontReqs.ADMVideo = append(beachfrontReqs.ADMVideo, videoList[i])
   246  			}
   247  		}
   248  	}
   249  
   250  	return
   251  }
   252  
   253  func getAppId(ext openrtb_ext.ExtImpBeachfront, media openrtb_ext.BidType) (string, error) {
   254  	var appid string
   255  	var error error
   256  
   257  	if ext.AppId != "" {
   258  		appid = ext.AppId
   259  	} else if media == openrtb_ext.BidTypeVideo && ext.AppIds.Video != "" {
   260  		appid = ext.AppIds.Video
   261  	} else if media == openrtb_ext.BidTypeBanner && ext.AppIds.Banner != "" {
   262  		appid = ext.AppIds.Banner
   263  	} else {
   264  		error = errors.New("unable to determine the appId(s) from the supplied extension")
   265  	}
   266  
   267  	return appid, error
   268  }
   269  
   270  func getSchain(request *openrtb2.BidRequest) (openrtb_ext.ExtRequestPrebidSChain, error) {
   271  	var schain openrtb_ext.ExtRequestPrebidSChain
   272  	return schain, json.Unmarshal(request.Source.Ext, &schain)
   273  }
   274  
   275  func getBannerRequest(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (beachfrontBannerRequest, []error) {
   276  	var bfr beachfrontBannerRequest
   277  	var errs = make([]error, 0, len(request.Imp))
   278  
   279  	for i := 0; i < len(request.Imp); i++ {
   280  
   281  		beachfrontExt, err := getBeachfrontExtension(request.Imp[i])
   282  
   283  		if err != nil {
   284  			errs = append(errs, err)
   285  			continue
   286  		}
   287  
   288  		appid, err := getAppId(beachfrontExt, openrtb_ext.BidTypeBanner)
   289  
   290  		if err != nil {
   291  			errs = append(errs, err)
   292  			continue
   293  		}
   294  
   295  		if fatal, err := setBidFloor(&beachfrontExt, &request.Imp[i], reqInfo); err != nil {
   296  			errs = append(errs, err)
   297  			if fatal {
   298  				continue
   299  			}
   300  		}
   301  
   302  		slot := beachfrontSlot{
   303  			Id:       appid,
   304  			Slot:     request.Imp[i].ID,
   305  			Bidfloor: request.Imp[i].BidFloor,
   306  		}
   307  
   308  		for j := 0; j < len(request.Imp[i].Banner.Format); j++ {
   309  
   310  			slot.Sizes = append(slot.Sizes, beachfrontSize{
   311  				H: uint64(request.Imp[i].Banner.Format[j].H),
   312  				W: uint64(request.Imp[i].Banner.Format[j].W),
   313  			})
   314  		}
   315  
   316  		bfr.Slots = append(bfr.Slots, slot)
   317  	}
   318  
   319  	if len(bfr.Slots) == 0 {
   320  		return bfr, errs
   321  	}
   322  
   323  	if request.Device != nil {
   324  		bfr.IP = request.Device.IP
   325  		bfr.DeviceModel = request.Device.Model
   326  		bfr.DeviceOs = request.Device.OS
   327  		if request.Device.DNT != nil {
   328  			bfr.Dnt = *request.Device.DNT
   329  		}
   330  		if request.Device.UA != "" {
   331  			bfr.UA = request.Device.UA
   332  		}
   333  	}
   334  
   335  	var t = fallBackDeviceType(request)
   336  
   337  	if t == adcom1.DeviceMobile {
   338  		bfr.Page = request.App.Bundle
   339  		if request.App.Domain == "" {
   340  			bfr.Domain = getDomain(request.App.Domain)
   341  		} else {
   342  			bfr.Domain = request.App.Domain
   343  		}
   344  
   345  		bfr.IsMobile = 1
   346  	} else if t == adcom1.DevicePC {
   347  		bfr.Page = request.Site.Page
   348  		if request.Site.Domain == "" {
   349  			bfr.Domain = getDomain(request.Site.Page)
   350  		} else {
   351  			bfr.Domain = request.Site.Domain
   352  		}
   353  
   354  		bfr.IsMobile = 0
   355  	}
   356  
   357  	bfr.Secure = isSecure(bfr.Page)
   358  
   359  	if request.User != nil && request.User.ID != "" {
   360  		if bfr.User.ID == "" {
   361  			bfr.User.ID = request.User.ID
   362  		}
   363  	}
   364  
   365  	if request.User != nil && request.User.BuyerUID != "" {
   366  		if bfr.User.BuyerUID == "" {
   367  			bfr.User.BuyerUID = request.User.BuyerUID
   368  		}
   369  	}
   370  
   371  	bfr.RequestID = request.ID
   372  	bfr.AdapterName = beachfrontAdapterName
   373  	bfr.AdapterVersion = beachfrontAdapterVersion
   374  
   375  	if request.Imp[0].Secure != nil {
   376  		bfr.Secure = *request.Imp[0].Secure
   377  	}
   378  	bfr.Real204 = true
   379  
   380  	if request.Source != nil && request.Source.Ext != nil {
   381  		schain, err := getSchain(request)
   382  		if err == nil {
   383  			bfr.SChain = schain.SChain
   384  		}
   385  	}
   386  
   387  	return bfr, errs
   388  }
   389  
   390  func fallBackDeviceType(request *openrtb2.BidRequest) adcom1.DeviceType {
   391  	if request.Site != nil {
   392  		return adcom1.DevicePC
   393  	}
   394  
   395  	return adcom1.DeviceMobile
   396  }
   397  
   398  func getVideoRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]beachfrontVideoRequest, []error) {
   399  	var bfReqs = make([]beachfrontVideoRequest, len(request.Imp))
   400  	var errs = make([]error, 0, len(request.Imp))
   401  	var failedRequestIndicies = make([]int, 0)
   402  
   403  	for i := 0; i < len(request.Imp); i++ {
   404  		beachfrontExt, err := getBeachfrontExtension(request.Imp[i])
   405  
   406  		if err != nil {
   407  			failedRequestIndicies = append(failedRequestIndicies, i)
   408  			errs = append(errs, err)
   409  			continue
   410  		}
   411  
   412  		appid, err := getAppId(beachfrontExt, openrtb_ext.BidTypeVideo)
   413  		bfReqs[i].AppId = appid
   414  
   415  		if err != nil {
   416  			failedRequestIndicies = append(failedRequestIndicies, i)
   417  			errs = append(errs, err)
   418  			continue
   419  		}
   420  
   421  		bfReqs[i].Request = *request
   422  		var secure int8
   423  
   424  		var deviceCopy openrtb2.Device
   425  		if bfReqs[i].Request.Device == nil {
   426  			deviceCopy = openrtb2.Device{}
   427  		} else {
   428  			deviceCopy = *bfReqs[i].Request.Device
   429  		}
   430  
   431  		if beachfrontExt.VideoResponseType == "nurl" {
   432  			bfReqs[i].VideoResponseType = "nurl"
   433  		} else {
   434  			bfReqs[i].VideoResponseType = "adm"
   435  
   436  			if deviceCopy.IP == "" {
   437  				deviceCopy.IP = fakeIP
   438  			}
   439  		}
   440  
   441  		if bfReqs[i].Request.Site != nil && bfReqs[i].Request.Site.Domain == "" && bfReqs[i].Request.Site.Page != "" {
   442  			siteCopy := *bfReqs[i].Request.Site
   443  			siteCopy.Domain = getDomain(bfReqs[i].Request.Site.Page)
   444  			bfReqs[i].Request.Site = &siteCopy
   445  			secure = isSecure(bfReqs[i].Request.Site.Page)
   446  		}
   447  
   448  		if bfReqs[i].Request.App != nil && bfReqs[i].Request.App.Domain == "" && bfReqs[i].Request.App.Bundle != "" {
   449  			if bfReqs[i].Request.App.Bundle != "" {
   450  				var chunks = strings.Split(strings.Trim(bfReqs[i].Request.App.Bundle, "_"), ".")
   451  
   452  				if len(chunks) > 1 {
   453  					appCopy := *bfReqs[i].Request.App
   454  					appCopy.Domain = fmt.Sprintf("%s.%s", chunks[len(chunks)-(len(chunks)-1)], chunks[0])
   455  					bfReqs[i].Request.App = &appCopy
   456  				}
   457  			}
   458  		}
   459  
   460  		if deviceCopy.DeviceType == 0 {
   461  			deviceCopy.DeviceType = fallBackDeviceType(request)
   462  		}
   463  		bfReqs[i].Request.Device = &deviceCopy
   464  
   465  		imp := request.Imp[i]
   466  
   467  		imp.Banner = nil
   468  		imp.Ext = nil
   469  		imp.Secure = &secure
   470  		if fatal, err := setBidFloor(&beachfrontExt, &imp, reqInfo); err != nil {
   471  			errs = append(errs, err)
   472  			if fatal {
   473  				failedRequestIndicies = append(failedRequestIndicies, i)
   474  				continue
   475  			}
   476  		}
   477  
   478  		if imp.Video.H == 0 && imp.Video.W == 0 {
   479  			imp.Video.W = defaultVideoWidth
   480  			imp.Video.H = defaultVideoHeight
   481  		}
   482  
   483  		if len(bfReqs[i].Request.Cur) == 0 {
   484  			bfReqs[i].Request.Cur = make([]string, 1)
   485  			bfReqs[i].Request.Cur[0] = "USD"
   486  		}
   487  
   488  		bfReqs[i].Request.Imp = nil
   489  		bfReqs[i].Request.Imp = make([]openrtb2.Imp, 1)
   490  		bfReqs[i].Request.Imp[0] = imp
   491  
   492  	}
   493  
   494  	if len(failedRequestIndicies) > 0 {
   495  		for i := 0; i < len(failedRequestIndicies); i++ {
   496  			bfReqs = removeVideoElement(bfReqs, failedRequestIndicies[i])
   497  		}
   498  
   499  	}
   500  	return bfReqs, errs
   501  }
   502  
   503  func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   504  	if response.StatusCode == http.StatusNoContent {
   505  		return nil, nil
   506  	}
   507  
   508  	if response.StatusCode >= http.StatusInternalServerError {
   509  		return nil, []error{&errortypes.BadServerResponse{
   510  			Message: fmt.Sprintf("server error status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri),
   511  		}}
   512  	}
   513  
   514  	if response.StatusCode >= http.StatusBadRequest {
   515  		return nil, []error{&errortypes.BadInput{
   516  			Message: fmt.Sprintf("request error status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri),
   517  		}}
   518  	}
   519  
   520  	if response.StatusCode != http.StatusOK {
   521  		return nil, []error{fmt.Errorf("unexpected status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri)}
   522  	}
   523  
   524  	var bids []openrtb2.Bid
   525  	var errs = make([]error, 0)
   526  	var xtrnal openrtb2.BidRequest
   527  
   528  	if err := json.Unmarshal(externalRequest.Body, &xtrnal); err != nil {
   529  		errs = append(errs, err)
   530  	} else {
   531  		bids, errs = postprocess(response, xtrnal, externalRequest.Uri, internalRequest.ID)
   532  	}
   533  
   534  	if len(errs) != 0 {
   535  		return nil, errs
   536  	}
   537  
   538  	var dur beachfrontVideoBidExtension
   539  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(BidCapacity)
   540  
   541  	for i := 0; i < len(bids); i++ {
   542  
   543  		if err := json.Unmarshal(bids[i].Ext, &dur); err == nil && dur.Duration > 0 {
   544  
   545  			impVideo := openrtb_ext.ExtBidPrebidVideo{
   546  				Duration: int(dur.Duration),
   547  			}
   548  
   549  			if len(bids[i].Cat) > 0 {
   550  				impVideo.PrimaryCategory = bids[i].Cat[0]
   551  			}
   552  
   553  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   554  				Bid:      &bids[i],
   555  				BidType:  a.getBidType(externalRequest),
   556  				BidVideo: &impVideo,
   557  			})
   558  		} else {
   559  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   560  				Bid:     &bids[i],
   561  				BidType: a.getBidType(externalRequest),
   562  			})
   563  		}
   564  	}
   565  
   566  	return bidResponse, errs
   567  }
   568  
   569  func setBidFloor(ext *openrtb_ext.ExtImpBeachfront, imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) (bool, error) {
   570  	var initialImpBidfloor float64 = imp.BidFloor
   571  	var err error
   572  
   573  	if imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" && imp.BidFloor > 0 {
   574  		imp.BidFloor, err = reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
   575  
   576  		var convertedFromCurrency = imp.BidFloorCur
   577  		imp.BidFloorCur = "USD"
   578  
   579  		if err != nil {
   580  			if ext.BidFloor > minBidFloor {
   581  				imp.BidFloor = ext.BidFloor
   582  				return false, &errortypes.Warning{
   583  					Message: fmt.Sprintf("The following error was recieved from the currency converter while attempting to convert the imp.bidfloor value of %.2f from %s to USD:\n%s\nThe provided value of imp.ext.beachfront.bidfloor, %.2f USD is being used as a fallback.",
   584  						initialImpBidfloor,
   585  						convertedFromCurrency,
   586  						err,
   587  						ext.BidFloor,
   588  					),
   589  				}
   590  			} else {
   591  				return true, &errortypes.BadInput{
   592  					Message: fmt.Sprintf("The following error was recieved from the currency converter while attempting to convert the imp.bidfloor value of %.2f from %s to USD:\n%s\nA value of imp.ext.beachfront.bidfloor was not provided. The bid is being skipped.",
   593  						initialImpBidfloor,
   594  						convertedFromCurrency,
   595  						err,
   596  					),
   597  				}
   598  			}
   599  		}
   600  	}
   601  
   602  	if imp.BidFloor < ext.BidFloor {
   603  		imp.BidFloor = ext.BidFloor
   604  	}
   605  
   606  	if imp.BidFloor > minBidFloor {
   607  		imp.BidFloorCur = "USD"
   608  	} else {
   609  		imp.BidFloor = 0
   610  		imp.BidFloorCur = ""
   611  	}
   612  
   613  	return false, nil
   614  }
   615  
   616  func (a *BeachfrontAdapter) getBidType(externalRequest *adapters.RequestData) openrtb_ext.BidType {
   617  	t := strings.Split(externalRequest.Uri, "=")[0]
   618  	if t == a.extraInfo.VideoEndpoint {
   619  		return openrtb_ext.BidTypeVideo
   620  	}
   621  
   622  	return openrtb_ext.BidTypeBanner
   623  }
   624  
   625  func postprocess(response *adapters.ResponseData, xtrnal openrtb2.BidRequest, uri string, id string) ([]openrtb2.Bid, []error) {
   626  	var beachfrontResp []beachfrontResponseSlot
   627  
   628  	var openrtbResp openrtb2.BidResponse
   629  
   630  	if err := json.Unmarshal(response.Body, &openrtbResp); err != nil || len(openrtbResp.SeatBid) == 0 {
   631  
   632  		if err := json.Unmarshal(response.Body, &beachfrontResp); err != nil {
   633  			return nil, []error{&errortypes.BadServerResponse{
   634  				Message: "server response failed to unmarshal as valid rtb. Run with request.debug = 1 for more info",
   635  			}}
   636  		} else {
   637  			return postprocessBanner(beachfrontResp, id)
   638  		}
   639  	}
   640  
   641  	return postprocessVideo(openrtbResp.SeatBid[0].Bid, xtrnal, uri, id)
   642  }
   643  
   644  func postprocessBanner(beachfrontResp []beachfrontResponseSlot, id string) ([]openrtb2.Bid, []error) {
   645  
   646  	var bids = make([]openrtb2.Bid, len(beachfrontResp))
   647  	var errs = make([]error, 0)
   648  
   649  	for i := 0; i < len(beachfrontResp); i++ {
   650  		bids[i] = openrtb2.Bid{
   651  			CrID:  beachfrontResp[i].CrID,
   652  			ImpID: beachfrontResp[i].Slot,
   653  			Price: beachfrontResp[i].Price,
   654  			ID:    fmt.Sprintf("%sBanner", beachfrontResp[i].Slot),
   655  			AdM:   beachfrontResp[i].Adm,
   656  			H:     int64(beachfrontResp[i].H),
   657  			W:     int64(beachfrontResp[i].W),
   658  		}
   659  	}
   660  
   661  	return bids, errs
   662  }
   663  
   664  func postprocessVideo(bids []openrtb2.Bid, xtrnal openrtb2.BidRequest, uri string, id string) ([]openrtb2.Bid, []error) {
   665  
   666  	var errs = make([]error, 0)
   667  
   668  	if uri[len(uri)-len(nurlVideoEndpointSuffix):] == nurlVideoEndpointSuffix {
   669  
   670  		for i := 0; i < len(bids); i++ {
   671  			crid := extractNurlVideoCrid(bids[i].NURL)
   672  
   673  			bids[i].CrID = crid
   674  			bids[i].ImpID = xtrnal.Imp[i].ID
   675  			bids[i].H = xtrnal.Imp[i].Video.H
   676  			bids[i].W = xtrnal.Imp[i].Video.W
   677  			bids[i].ID = fmt.Sprintf("%sNurlVideo", xtrnal.Imp[i].ID)
   678  		}
   679  
   680  	} else {
   681  		for i := 0; i < len(bids); i++ {
   682  			bids[i].ID = fmt.Sprintf("%sAdmVideo", bids[i].ImpID)
   683  		}
   684  
   685  	}
   686  	return bids, errs
   687  }
   688  
   689  func extractNurlVideoCrid(nurl string) string {
   690  	chunky := strings.SplitAfter(nurl, ":")
   691  	if len(chunky) > 1 {
   692  		return strings.TrimSuffix(chunky[2], ":")
   693  	}
   694  
   695  	return ""
   696  }
   697  
   698  func getBeachfrontExtension(imp openrtb2.Imp) (openrtb_ext.ExtImpBeachfront, error) {
   699  	var err error
   700  	var bidderExt adapters.ExtImpBidder
   701  	var beachfrontExt openrtb_ext.ExtImpBeachfront
   702  
   703  	if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   704  		return beachfrontExt, &errortypes.BadInput{
   705  			Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err),
   706  		}
   707  	}
   708  
   709  	if err = json.Unmarshal(bidderExt.Bidder, &beachfrontExt); err != nil {
   710  		return beachfrontExt, &errortypes.BadInput{
   711  			Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBeachfront, err: %s", imp.ID, err),
   712  		}
   713  	}
   714  
   715  	return beachfrontExt, err
   716  }
   717  
   718  func getDomain(page string) string {
   719  	protoURL := strings.Split(page, "//")
   720  	var domainPage string
   721  
   722  	if len(protoURL) > 1 {
   723  		domainPage = protoURL[1]
   724  	} else {
   725  		domainPage = protoURL[0]
   726  	}
   727  
   728  	return strings.Split(domainPage, "/")[0]
   729  
   730  }
   731  
   732  func isSecure(page string) int8 {
   733  	protoURL := strings.Split(page, "://")
   734  
   735  	if len(protoURL) > 1 && protoURL[0] == "https" {
   736  		return 1
   737  	}
   738  
   739  	return 0
   740  
   741  }
   742  
   743  func removeVideoElement(slice []beachfrontVideoRequest, s int) []beachfrontVideoRequest {
   744  	if len(slice) >= s+1 {
   745  		return append(slice[:s], slice[s+1:]...)
   746  	}
   747  
   748  	return []beachfrontVideoRequest{}
   749  }
   750  
   751  // Builder builds a new instance of the Beachfront adapter for the given bidder with the given config.
   752  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   753  	extraInfo, err := getExtraInfo(config.ExtraAdapterInfo)
   754  	if err != nil {
   755  		return nil, err
   756  	}
   757  
   758  	bidder := &BeachfrontAdapter{
   759  		bannerEndpoint: config.Endpoint,
   760  		extraInfo:      extraInfo,
   761  	}
   762  	return bidder, nil
   763  }
   764  
   765  func getExtraInfo(v string) (ExtraInfo, error) {
   766  	if len(v) == 0 {
   767  		return getDefaultExtraInfo(), nil
   768  	}
   769  
   770  	var extraInfo ExtraInfo
   771  	if err := json.Unmarshal([]byte(v), &extraInfo); err != nil {
   772  		return extraInfo, fmt.Errorf("invalid extra info: %v", err)
   773  	}
   774  
   775  	if extraInfo.VideoEndpoint == "" {
   776  		extraInfo.VideoEndpoint = defaultVideoEndpoint
   777  	}
   778  
   779  	return extraInfo, nil
   780  }
   781  
   782  func getDefaultExtraInfo() ExtraInfo {
   783  	return ExtraInfo{
   784  		VideoEndpoint: defaultVideoEndpoint,
   785  	}
   786  }