├── .gitignore ├── LICENSE ├── README.md ├── akamai.go ├── akamai ├── internal │ ├── radix.go │ └── radix_test.go ├── pixel.go ├── pixel_test.go ├── script_path.go ├── script_path_test.go ├── sec_cpt.go ├── sec_cpt_easyjson.go ├── sec_cpt_test.go ├── stop_signal.go └── stop_signal_test.go ├── api.go ├── datadome.go ├── datadome └── parse.go ├── go.mod ├── go.sum ├── incapsula.go ├── incapsula ├── dynamic.go ├── dynamic_test.go ├── utmvc.go └── utmvc_test.go ├── internal ├── decompress.go └── zstd.go ├── kasada.go ├── kasada └── script_path.go ├── models.go ├── models_easyjson.go └── session.go /.gitignore: -------------------------------------------------------------------------------- 1 | /scripts/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Hyper Solutions 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hyper Solutions SDK 2 | 3 | ## Installation 4 | 5 | To use the Hyper Solutions SDK in your Go project, you need to install it using the following command: 6 | 7 | ``` 8 | go get github.com/Hyper-Solutions/hyper-sdk-go/v2 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### Creating a Session 14 | 15 | To start using the SDK, you need to create a new `Session` instance by providing your API key: 16 | 17 | ```go 18 | session := hyper.NewSession("your-api-key") 19 | ``` 20 | 21 | You can also optionally set a JWT key and a custom HTTP client: 22 | 23 | ```go 24 | session := hyper.NewSession("your-api-key"). 25 | WithJwtKey("your-jwt-key"). 26 | WithClient(customHTTPClient) 27 | ``` 28 | 29 | ## Akamai 30 | 31 | The Akamai package provides functions for interacting with Akamai Bot Manager, including generating sensor data, parsing script path, parsing pixel challenges, and handling sec-cpt challenges. 32 | 33 | ### Generating Sensor Data 34 | 35 | To generate sensor data required for generating valid Akamai cookies, use the `GenerateSensorData` function: 36 | 37 | ```go 38 | sensorData, err := session.GenerateSensorData(ctx, &hyper.SensorInput{ 39 | // Set the required input fields 40 | }) 41 | if err != nil { 42 | // Handle the error 43 | } 44 | ``` 45 | 46 | ### Parsing Script Path 47 | 48 | To parse the Akamai Bot Manager script path from the given HTML code, use the `ParseScriptPath` function: 49 | 50 | ```go 51 | scriptPath, err := akamai.ParseScriptPath(reader) 52 | if err != nil { 53 | // Handle the error 54 | } 55 | ``` 56 | 57 | ### Handling Sec-Cpt Challenges 58 | 59 | The Akamai package provides functions for handling sec-cpt challenges: 60 | 61 | - `ParseSecCptChallenge`: Parses a sec-cpt challenge from an `io.Reader`. 62 | - `ParseSecCptChallengeFromJson`: Parses a sec-cpt challenge from an `io.Reader`. 63 | - `GenerateSecCptPayload`: Generates a sec-cpt payload using the provided sec-cpt cookie. 64 | - `Sleep`: Sleeps for the duration specified in the sec-cpt challenge. 65 | - `SleepWithContext`: Sleeps for the duration specified in the sec-cpt challenge, this is context aware. 66 | 67 | ### Validating Cookies 68 | 69 | The Akamai package provides functions for validating cookies: 70 | 71 | - `IsCookieValid`: Determines if the provided `_abck` cookie value is valid based on the given request count. 72 | - `IsCookieInvalidated`: Determines if the current session requires more sensors to be sent. 73 | 74 | 75 | ### Generating Pixel Data 76 | 77 | To generate pixel data, use the `GeneratePixelData` function: 78 | 79 | ```go 80 | pixelData, err := session.GeneratePixelData(ctx, &hyper.PixelInput{ 81 | // Set the required input fields 82 | }) 83 | if err != nil { 84 | // Handle the error 85 | } 86 | ``` 87 | 88 | ### Parsing Pixel Challenges 89 | 90 | The Akamai package provides functions for parsing pixel challenges: 91 | 92 | - `ParsePixelHtmlVar`: Parses the required pixel challenge variable from the given HTML code. 93 | - `ParsePixelScriptURL`: Parses the script URL of the pixel challenge script and the URL to post a generated payload to from the given HTML code. 94 | - `ParsePixelScriptVar`: Parses the dynamic value from the pixel script. 95 | ## Incapsula 96 | 97 | The Incapsula package provides functions for interacting with Incapsula, including generating Reese84 sensor data, UTMVC cookies, and parsing UTMVC script paths. 98 | 99 | ### Generating Reese84 Sensor 100 | 101 | To generate sensor data required for generating valid Reese84 cookies, use the `GenerateReese84Sensor` function: 102 | 103 | ```go 104 | sensorData, err := session.GenerateReese84Sensor(ctx, site, userAgent) 105 | if err != nil { 106 | // Handle the error 107 | } 108 | ``` 109 | 110 | ### Generating UTMVC Cookie 111 | 112 | To generate the UTMVC cookie using the Hyper Solutions API, use the `GenerateUtmvcCookie` function: 113 | 114 | ```go 115 | utmvcCookie, err := session.GenerateUtmvcCookie(ctx, &hyper.UtmvcInput{ 116 | Script: "your-script", 117 | SessionIds: []string{"session-id-1", "session-id-2"}, 118 | UserAgent: "user-agent-here" 119 | }) 120 | if err != nil { 121 | // Handle the error 122 | } 123 | ``` 124 | 125 | ### Parsing UTMVC Script Path 126 | 127 | To parse the UTMVC script path from a given script content, use the `ParseUtmvcScriptPath` function: 128 | 129 | ```go 130 | scriptPath, err := incapsula.ParseUtmvcScriptPath(scriptReader) 131 | if err != nil { 132 | // Handle the error 133 | } 134 | ``` 135 | 136 | ### Generating UTMVC Submit Path 137 | 138 | To generate a unique UTMVC submit path with a random query parameter, use the `GetUtmvcSubmitPath` function: 139 | 140 | ```go 141 | submitPath := incapsula.GetUtmvcSubmitPath() 142 | ``` 143 | 144 | ## Kasada 145 | 146 | The Kasada package provides functions for interacting with Kasada Bot Manager, including parsing script path. 147 | 148 | ### Generating Payload Data (CT) 149 | 150 | To generate payload data required for generating valid `x-kpsdk-ct` tokens, use the `GenerateKasadaPayload` function: 151 | 152 | ```go 153 | payload, headers, err := session.GenerateKasadaPayload(ctx, &hyper.KasadaPayloadInput{ 154 | // Set the required input fields 155 | }) 156 | if err != nil { 157 | // Handle the error 158 | } 159 | ``` 160 | 161 | ### Generating Pow Data (CD) 162 | 163 | To generate POW data (`x-kpsdk-cd`) tokens, use the `GenerateKasadaPow` function: 164 | 165 | ```go 166 | payload, err := session.GenerateKasadaPow(ctx, &hyper.KasadaPowInput{ 167 | // Set the required input fields 168 | }) 169 | if err != nil { 170 | // Handle the error 171 | } 172 | ``` 173 | 174 | ### Parsing Script Path 175 | 176 | To parse the Kasada script path from the given blocked page (status code 429) HTML code, use the `ParseScriptPath` function: 177 | 178 | ```go 179 | scriptPath, err := kasada.ParseScriptPath(reader) 180 | if err != nil { 181 | // Handle the error 182 | } 183 | // will look like: /ips.js?... 184 | ``` 185 | 186 | ## DataDome 187 | 188 | The DataDome package provides functions for interacting with DataDome Bot Manager, including parsing device link URLs 189 | for interstitial and slider. 190 | 191 | ### Generating Interstitial Payload 192 | 193 | To generate payload data required for solving interstitial, use the `GenerateDataDomeInterstitial` function: 194 | 195 | ```go 196 | payload, headers, err := session.GenerateDataDomeInterstitial(ctx, &hyper.DataDomeInterstitialInput{ 197 | // Set the required input fields 198 | }) 199 | if err != nil { 200 | // Handle the error 201 | } 202 | // Use the payload to POST to https://geo.captcha-delivery.com/interstitial/ 203 | ``` 204 | 205 | ### Generating Slider Payload 206 | 207 | To solve DataDome Slider, use the `GenerateDataDomeSlider` function: 208 | 209 | ```go 210 | checkUrl, headers, err := session.GenerateDataDomeSlider(ctx, &hyper.DataDomeSliderInput{ 211 | // Set the required input fields 212 | }) 213 | if err != nil { 214 | // Handle the error 215 | } 216 | // Create a GET request to the checkUrl 217 | ``` 218 | 219 | ### Parsing Interstitial DeviceLink URL 220 | 221 | To parse the Interstitial DeviceLink URL from the HTML code, use the `ParseInterstitialDeviceCheckLink` function: 222 | 223 | ```go 224 | deviceLink, err := datadome.ParseInterstitialDeviceCheckLink(reader, datadomeCookie, referer) 225 | if err != nil { 226 | // Handle the error 227 | } 228 | // deviceLink will look like: https://geo.captcha-delivery.com/interstitial/?... 229 | ``` 230 | 231 | ### Parsing Slider DeviceLink URL 232 | 233 | To parse the Slider DeviceLink URL from the HTML code, use the `ParseSliderDeviceCheckLink` function: 234 | 235 | ```go 236 | deviceLink, err := datadome.ParseSliderDeviceCheckLink(reader, datadomeCookie, referer) 237 | if err != nil { 238 | // Handle the error 239 | } 240 | // deviceLink will look like: https://geo.captcha-delivery.com/captcha/?... 241 | ``` 242 | 243 | ## Contributing 244 | 245 | If you find any issues or have suggestions for improvement, please open an issue or submit a pull request. 246 | 247 | ## License 248 | 249 | This SDK is licensed under the [MIT License](LICENSE). 250 | -------------------------------------------------------------------------------- /akamai.go: -------------------------------------------------------------------------------- 1 | package hyper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | // GenerateSensorData returns the sensor data required to generate valid akamai cookies using the Hyper Solutions API. 9 | func (s *Session) GenerateSensorData(ctx context.Context, input *SensorInput) (string, string, error) { 10 | response, err := sendRequest[*SensorInput, *apiResponse](ctx, s, "https://akm.hypersolutions.co/v2/sensor", input) 11 | if err != nil { 12 | return "", "", err 13 | } 14 | if response.Error != "" { 15 | return "", "", fmt.Errorf("api returned with: %s", response.Error) 16 | } 17 | 18 | return response.Payload, response.Context, nil 19 | } 20 | 21 | // ParseV3Dynamic returns the dynamic values for a v3 dynamic script 22 | func (s *Session) ParseV3Dynamic(ctx context.Context, input *DynamicInput) (string, error) { 23 | response, err := sendRequest[*DynamicInput, *apiResponse](ctx, s, "https://akm.hypersolutions.co/v3dynamic", input) 24 | if err != nil { 25 | return "", err 26 | } 27 | if response.Error != "" { 28 | return "", fmt.Errorf("api returned with: %s", response.Error) 29 | } 30 | 31 | return response.Payload, nil 32 | } 33 | 34 | // GeneratePixelData returns the pixel data using the Hyper Solutions API. 35 | func (s *Session) GeneratePixelData(ctx context.Context, input *PixelInput) (string, error) { 36 | response, err := sendRequest[*PixelInput, *apiResponse](ctx, s, "https://akm.hypersolutions.co/pixel", input) 37 | if err != nil { 38 | return "", err 39 | } 40 | if response.Error != "" { 41 | return "", fmt.Errorf("api returned with: %s", response.Error) 42 | } 43 | 44 | return response.Payload, nil 45 | } 46 | 47 | // GenerateSbsdData returns the sbsd payload using the Hyper Solutions API. 48 | func (s *Session) GenerateSbsdData(ctx context.Context, input *SbsdInput) (string, error) { 49 | response, err := sendRequest[*SbsdInput, *apiResponse](ctx, s, "https://akm.hypersolutions.co/sbsd", input) 50 | if err != nil { 51 | return "", err 52 | } 53 | if response.Error != "" { 54 | return "", fmt.Errorf("api returned with: %s", response.Error) 55 | } 56 | 57 | return response.Payload, nil 58 | } 59 | -------------------------------------------------------------------------------- /akamai/internal/radix.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | const ( 8 | kBufferSize = 2200 9 | // 0x7FF0'0000'0000'0000 10 | kExponentMask = 9218868437227405312 11 | kPhysicalSignificandSize = 52 12 | kExponentBias = 0x3FF + kPhysicalSignificandSize 13 | chars = "0123456789abcdefghijklmnopqrstuvwxyz" 14 | ) 15 | 16 | // FloatToStringRadix Translated from https://github.com/v8/v8/blob/master/src/numbers/conversions.cc#L1269 17 | // and https://github.com/v8/v8/blob/master/src/numbers/double.h 18 | func FloatToStringRadix(value float64, radix int, buf []byte) int { 19 | buffer := make([]byte, kBufferSize) 20 | integerCursor := kBufferSize / 2 21 | fractionCursor := integerCursor 22 | 23 | negative := value < 0 24 | if negative { 25 | value = -value 26 | } 27 | 28 | integer := math.Floor(value) 29 | fraction := value - integer 30 | delta := 0.5 * (double(value).NextDouble() - value) 31 | delta = math.Max(double(0.0).NextDouble(), delta) 32 | 33 | if fraction >= delta { 34 | buffer[fractionCursor] = 46 35 | fractionCursor++ 36 | 37 | for { 38 | fraction *= float64(radix) 39 | delta *= float64(radix) 40 | 41 | digit := int(fraction) 42 | buffer[fractionCursor] = chars[digit] 43 | fractionCursor++ 44 | 45 | fraction -= float64(digit) 46 | 47 | if fraction > 0.5 || (fraction == 0.5 && ((digit & 1) != 0)) { 48 | if fraction+delta > 1 { 49 | for { 50 | fractionCursor-- 51 | if fractionCursor == kBufferSize/2 { 52 | integer += 1 53 | break 54 | } 55 | 56 | c := buffer[fractionCursor] 57 | var digit byte 58 | if c > 57 { 59 | digit = c - 97 + 10 60 | } else { 61 | digit = c - 48 62 | } 63 | 64 | if int(digit+1) < radix { 65 | buffer[fractionCursor] = chars[digit+1] 66 | fractionCursor++ 67 | break 68 | } 69 | } 70 | break 71 | } 72 | } 73 | 74 | if !(fraction >= delta) { 75 | break 76 | } 77 | } 78 | } 79 | 80 | for double(integer/float64(radix)).Exponent() > 0 { 81 | integer /= float64(radix) 82 | integerCursor-- 83 | buffer[integerCursor] = 48 84 | } 85 | 86 | for { 87 | remainder := math.Remainder(integer, float64(radix)) 88 | integerCursor-- 89 | buffer[integerCursor] = chars[int(remainder)] 90 | integer = (integer - remainder) / float64(radix) 91 | 92 | if !(integer > 0) { 93 | break 94 | } 95 | } 96 | 97 | if negative { 98 | integerCursor-- 99 | buffer[integerCursor] = 45 100 | } 101 | 102 | //copy(buf[offset:], buffer[integerCursor:fractionCursor]) 103 | copy(buf, buffer[integerCursor:fractionCursor]) 104 | return fractionCursor - integerCursor 105 | } 106 | 107 | type double float64 108 | 109 | func (d double) AsUint64() uint64 { 110 | return math.Float64bits(float64(d)) 111 | } 112 | 113 | func (d double) NextDouble() float64 { 114 | d64 := d.AsUint64() 115 | 116 | // When the original float is negative, you must decrement 117 | // the uint to get the next greater double when converting back 118 | if d < 0 { 119 | return math.Float64frombits(d64 - 1) 120 | } 121 | 122 | return math.Float64frombits(d64 + 1) 123 | } 124 | 125 | func (d double) Exponent() int { 126 | d64 := d.AsUint64() 127 | 128 | biasedE := math.Float64bits(float64((d64 & kExponentMask) >> kPhysicalSignificandSize)) 129 | return int(biasedE) - kExponentBias 130 | } 131 | -------------------------------------------------------------------------------- /akamai/internal/radix_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | const correctOutput = "0.da49605db64e5" 9 | 10 | func TestFloatToStringRadix16(t *testing.T) { 11 | buf := make([]byte, 18) 12 | length := FloatToStringRadix(0.852682135466517, 16, buf) 13 | v := string(buf[:length]) 14 | if v != correctOutput { 15 | t.Errorf("expected %v, got: %v", correctOutput, v) 16 | } 17 | } 18 | 19 | func BenchmarkFloatToStringRadix16(b *testing.B) { 20 | buf := make([]byte, 18) 21 | 22 | b.ReportAllocs() 23 | for n := 0; n < b.N; n++ { 24 | FloatToStringRadix(rand.Float64(), 16, buf) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /akamai/pixel.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | pixelHtmlExpr = regexp.MustCompile(`bazadebezolkohpepadr="(\d+)"`) 13 | pixelScriptUrlExpr = regexp.MustCompile(`src="(https?://.+/akam/\d+/\w+)"`) 14 | pixelScriptVarExpr = regexp.MustCompile(`g=_\[(\d+)]`) 15 | pixelScriptStringArrayExpr = regexp.MustCompile(`var _=\[(.+)];`) 16 | pixelScriptStringsExpr = regexp.MustCompile(`("[^",]*")`) 17 | 18 | ErrPixelHtmlVarNotFound = errors.New("hyper-sdk: pixel HTML var not found") 19 | ErrPixelScriptUrlNotFound = errors.New("hyper-sdk: script URL not found") 20 | ErrPixelScriptVarNotFound = errors.New("hyper-sdk: script var not found") 21 | ) 22 | 23 | // ParsePixelHtmlVar gets the required pixel challenge variable from the given HTML code src. 24 | func ParsePixelHtmlVar(reader io.Reader) (int, error) { 25 | src, err := io.ReadAll(reader) 26 | if err != nil { 27 | return 0, errors.Join(ErrPixelHtmlVarNotFound, err) 28 | } 29 | 30 | matches := pixelHtmlExpr.FindSubmatch(src) 31 | if len(matches) < 2 { 32 | return 0, ErrPixelHtmlVarNotFound 33 | } 34 | 35 | if v, err := strconv.Atoi(string(matches[1])); err == nil { 36 | return v, nil 37 | } else { 38 | return 0, errors.Join(ErrPixelHtmlVarNotFound, err) 39 | } 40 | } 41 | 42 | // ParsePixelScriptURL gets the script URL of the pixel challenge script and the URL 43 | // to post a generated payload to from the given HTML code src. 44 | func ParsePixelScriptURL(reader io.Reader) (string, string, error) { 45 | src, err := io.ReadAll(reader) 46 | if err != nil { 47 | return "", "", errors.Join(ErrPixelScriptUrlNotFound, err) 48 | } 49 | 50 | matches := pixelScriptUrlExpr.FindSubmatch(src) 51 | if len(matches) < 2 { 52 | return "", "", errors.Join(ErrPixelScriptUrlNotFound, err) 53 | } 54 | 55 | scriptUrl := string(matches[1]) 56 | 57 | // Create postUrl 58 | parts := strings.Split(scriptUrl, "/") 59 | parts[len(parts)-1] = "pixel_" + parts[len(parts)-1] 60 | postUrl := strings.Join(parts, "/") 61 | 62 | return scriptUrl, postUrl, nil 63 | } 64 | 65 | // ParsePixelScriptVar gets the dynamic value from the pixel script 66 | func ParsePixelScriptVar(reader io.Reader) (string, error) { 67 | src, err := io.ReadAll(reader) 68 | if err != nil { 69 | return "", errors.Join(ErrPixelScriptVarNotFound, err) 70 | } 71 | 72 | index := pixelScriptVarExpr.FindSubmatch(src) 73 | if len(index) < 2 { 74 | return "", ErrPixelScriptVarNotFound 75 | } 76 | stringIndex, err := strconv.Atoi(string(index[1])) 77 | if err != nil { 78 | return "", ErrPixelScriptVarNotFound 79 | } 80 | 81 | arrayDeclaration := pixelScriptStringArrayExpr.FindSubmatch(src) 82 | if len(arrayDeclaration) < 2 { 83 | return "", ErrPixelScriptVarNotFound 84 | } 85 | 86 | rawStrings := pixelScriptStringsExpr.FindAllSubmatch(arrayDeclaration[1], -1) 87 | if stringIndex >= len(rawStrings) { 88 | return "", ErrPixelScriptVarNotFound 89 | } 90 | 91 | if len(rawStrings[stringIndex]) < 2 { 92 | return "", ErrPixelScriptVarNotFound 93 | } 94 | 95 | if v, err := strconv.Unquote(string(rawStrings[stringIndex][1])); err == nil { 96 | return v, nil 97 | } else { 98 | return "", errors.Join(ErrPixelScriptVarNotFound, err) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /akamai/pixel_test.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestParsePixelHtmlVar(t *testing.T) { 10 | if v, err := ParsePixelHtmlVar(strings.NewReader(``)); err != nil { 11 | t.Fatal(err) 12 | } else { 13 | t.Log(v) 14 | } 15 | } 16 | 17 | func TestParsePixelScriptURL(t *testing.T) { 18 | if scriptUrl, postUrl, err := ParsePixelScriptURL(strings.NewReader(`src="https://www.example.com/akam/13/c88db65"`)); err != nil { 19 | t.Fatal(err) 20 | } else { 21 | t.Log(scriptUrl, postUrl) 22 | } 23 | } 24 | 25 | func BenchmarkParsePixelScriptURL(b *testing.B) { 26 | script := strings.NewReader(`src="https://www.example.com/akam/13/c88db65"`) 27 | 28 | b.ResetTimer() 29 | b.ReportAllocs() 30 | for i := 0; i < b.N; i++ { 31 | _, _, err := ParsePixelScriptURL(script) 32 | if err != nil { 33 | b.Fatal(err) 34 | } 35 | 36 | script.Seek(0, io.SeekStart) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /akamai/script_path.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "regexp" 7 | ) 8 | 9 | var ( 10 | scriptPathExpr = regexp.MustCompile(``) 11 | 12 | ErrScriptPathNotFound = errors.New("hyper-sdk: script path not found") 13 | ) 14 | 15 | // ParseScriptPath gets the Akamai Bot Manager web SDK path from the given HTML code src. 16 | func ParseScriptPath(reader io.Reader) (string, error) { 17 | src, err := io.ReadAll(reader) 18 | if err != nil { 19 | return "", errors.Join(ErrScriptPathNotFound, err) 20 | } 21 | 22 | matches := scriptPathExpr.FindSubmatch(src) 23 | if len(matches) < 2 { 24 | return "", ErrScriptPathNotFound 25 | } 26 | 27 | return string(matches[1]), nil 28 | } 29 | -------------------------------------------------------------------------------- /akamai/script_path_test.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestParseScriptPath(t *testing.T) { 10 | path, err := ParseScriptPath(strings.NewReader(``)) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | 15 | t.Log(path) 16 | } 17 | 18 | func BenchmarkParseScriptPath(b *testing.B) { 19 | script := strings.NewReader(``) 20 | 21 | b.ResetTimer() 22 | b.ReportAllocs() 23 | for i := 0; i < b.N; i++ { 24 | _, err := ParseScriptPath(script) 25 | if err != nil { 26 | b.Fatal(err) 27 | } 28 | 29 | script.Seek(0, io.SeekStart) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /akamai/sec_cpt.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "context" 5 | "crypto/sha256" 6 | "encoding/base64" 7 | "errors" 8 | "github.com/mailru/easyjson" 9 | "io" 10 | "math/rand" 11 | "regexp" 12 | "strconv" 13 | "strings" 14 | "time" 15 | 16 | "github.com/Hyper-Solutions/hyper-sdk-go/v2/akamai/internal" 17 | "github.com/Hyper-Solutions/orderedobject" 18 | jsoniter "github.com/json-iterator/go" 19 | ) 20 | 21 | var ( 22 | secDurationExpr = regexp.MustCompile(`data-duration=(\d+)`) 23 | secChallengeExpr = regexp.MustCompile(`challenge="(.*?)"`) 24 | secPageExpr = regexp.MustCompile(`data-duration=\d+\s+src="([^"]+)"`) 25 | 26 | ErrSecCptParsing = errors.New("hyper-sdk: error parsing sec-cpt") 27 | ErrSecCptInvalidCookie = errors.New("hyper-sdk: malformed sec_cpt cookie") 28 | ) 29 | 30 | type SecCptChallenge struct { 31 | ChallengePath string 32 | 33 | duration int 34 | challengeData *secCptChallengeData 35 | } 36 | 37 | //easyjson:json 38 | type secCptChallengeData struct { 39 | Token string `json:"token"` 40 | Timestamp int `json:"timestamp"` 41 | Nonce string `json:"nonce"` 42 | Difficulty int `json:"difficulty"` 43 | Count int `json:"count"` 44 | Timeout int `json:"timeout"` 45 | CPU bool `json:"cpu"` 46 | VerifyURL string `json:"verify_url"` 47 | } 48 | 49 | //easyjson:json 50 | type secCptApiResponse struct { 51 | SecCpChallenge string `json:"sec-cp-challenge"` 52 | Provider string `json:"provider"` 53 | BrandingURLContent string `json:"branding_url_content"` 54 | ChlgDuration int `json:"chlg_duration"` 55 | Token string `json:"token"` 56 | Timestamp int `json:"timestamp"` 57 | Nonce string `json:"nonce"` 58 | Difficulty int `json:"difficulty"` 59 | Timeout int `json:"timeout"` 60 | CPU bool `json:"cpu"` 61 | } 62 | 63 | // ParseSecCptChallenge parses a sec-cpt challenge from an io.Reader. 64 | // 65 | // The function extracts the challenge data, duration, and challenge path from the provided HTML content. 66 | // It returns a *SecCptChallenge struct containing the parsed information and any error encountered during parsing. 67 | // 68 | // Example usage: 69 | // 70 | // html := `` 71 | // challenge, err := ParseSecCptChallenge(strings.NewReader(html)) 72 | // if err != nil { 73 | // // Handle the error 74 | // } 75 | // 76 | // Parameters: 77 | // - reader: An io.Reader containing the HTML content with the sec-cpt challenge. 78 | // 79 | // Returns: 80 | // - *SecCptChallenge: A pointer to a SecCptChallenge struct containing the parsed challenge data, duration, and challenge path. 81 | // - error: An error encountered during parsing, or nil if parsing was successful. 82 | // 83 | // Errors: 84 | // - ErrSecCptParsing: Returned when there is an error parsing the sec-cpt challenge data. 85 | // - Other errors may be returned by the underlying io.Reader or JSON unmarshaling. 86 | func ParseSecCptChallenge(html io.Reader) (*SecCptChallenge, error) { 87 | src, err := io.ReadAll(html) 88 | if err != nil { 89 | return nil, errors.Join(ErrSecCptParsing, err) 90 | } 91 | 92 | challengeData, err := parseSecCptChallengeData(src) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | duration, err := parseSecCptDuration(src) 98 | if err != nil { 99 | return nil, err 100 | } 101 | 102 | challengePath, err := parseSecCptChallengePath(src) 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | return &SecCptChallenge{ 108 | challengeData: challengeData, 109 | duration: duration, 110 | ChallengePath: challengePath, 111 | }, nil 112 | } 113 | 114 | // ParseSecCptChallengeFromJson parses a sec-cpt challenge from a JSON payload. 115 | // 116 | // The function takes an io.Reader containing the JSON payload of the sec-cpt challenge 117 | // and unmarshals it into a secCptApiResponse struct. It then extracts the necessary 118 | // information from the struct to create a SecCptChallenge struct. 119 | // 120 | // Example usage: 121 | // 122 | // jsonPayload := `{"sec-cp-challenge":"true","provider":"crypto","branding_url_content":"/_sec/cp_challenge/crypto_message-4-3.htm","chlg_duration":30,"token":"AAQAAAAJ____9z_ZPsdHbk36hg2f6np2sGJDXmkwGmBiMBr_DDEmSWfi8Zt7BdtjWrNd9KD4DS_vim0VnK2wsa8tIC7XWsCshkvDF9J9Rf5EFwBU00c6SMXTaSNSTcDR-HVFGp3uAa67Mb3I6HeifXbjALcEomjcnwa9ZNQdDWuTAUTgNGbYw09A8AXIuP9DNv3QktUx488FV38Rm6xBXr66-MmD05hsBhucIYpLS_VCJVs9OFPnWsksPJ19ibw2K3fabfJbzIdB3Xv3J0kzLQ0gY7bpLRXK1oAcUTxNNsy-LQGe_lyV6INQ4ojPLGJpOTk","timestamp":1713283747,"nonce":"ebccdb479fcb92636fbc","difficulty":15000,"timeout":1000,"cpu":false}` 123 | // challenge, err := ParseSecCptChallengeFromJson(strings.NewReader(jsonPayload)) 124 | // if err != nil { 125 | // // Handle the error 126 | // } 127 | // 128 | // Parameters: 129 | // - payload: An io.Reader containing the JSON payload of the sec-cpt challenge. 130 | // 131 | // Returns: 132 | // - *SecCptChallenge: A pointer to a SecCptChallenge struct containing the parsed challenge data, duration, and challenge path. 133 | // - error: An error encountered during parsing, or nil if parsing was successful. 134 | // 135 | // Errors: 136 | // - Any error returned by the JSON unmarshaling process. 137 | func ParseSecCptChallengeFromJson(payload io.Reader) (*SecCptChallenge, error) { 138 | var apiResponse secCptApiResponse 139 | if err := easyjson.UnmarshalFromReader(payload, &apiResponse); err != nil { 140 | return nil, err 141 | } 142 | 143 | return &SecCptChallenge{ 144 | challengeData: &secCptChallengeData{ 145 | Token: apiResponse.Token, 146 | Timestamp: apiResponse.Timestamp, 147 | Nonce: apiResponse.Nonce, 148 | Difficulty: apiResponse.Difficulty, 149 | Timeout: apiResponse.Timeout, 150 | }, 151 | duration: apiResponse.ChlgDuration, 152 | ChallengePath: apiResponse.BrandingURLContent, 153 | }, nil 154 | } 155 | 156 | func parseSecCptChallengeData(src []byte) (*secCptChallengeData, error) { 157 | challengeMatches := secChallengeExpr.FindSubmatch(src) 158 | if len(challengeMatches) < 2 { 159 | return nil, ErrSecCptParsing 160 | } 161 | 162 | decodedChallenge := make([]byte, base64.StdEncoding.DecodedLen(len(challengeMatches[1]))) 163 | n, err := base64.StdEncoding.Decode(decodedChallenge, challengeMatches[1]) 164 | if err != nil { 165 | return nil, err 166 | } 167 | 168 | var cd secCptChallengeData 169 | if err := easyjson.Unmarshal(decodedChallenge[:n], &cd); err != nil { 170 | return nil, err 171 | } 172 | 173 | return &cd, nil 174 | } 175 | 176 | func parseSecCptDuration(src []byte) (int, error) { 177 | durationMatches := secDurationExpr.FindSubmatch(src) 178 | if len(durationMatches) < 2 { 179 | return 0, ErrSecCptParsing 180 | } 181 | 182 | duration, err := strconv.Atoi(string(durationMatches[1])) 183 | if err != nil { 184 | return 0, errors.Join(ErrSecCptParsing, err) 185 | } 186 | 187 | return duration, nil 188 | } 189 | 190 | func parseSecCptChallengePath(src []byte) (string, error) { 191 | pageMatches := secPageExpr.FindSubmatch(src) 192 | if len(pageMatches) < 2 { 193 | return "", ErrSecCptParsing 194 | } 195 | 196 | return string(pageMatches[1]), nil 197 | } 198 | 199 | // GenerateSecCptPayload generates the payload for the sec-cpt challenge. 200 | // 201 | // The function takes the sec_cpt cookie value as input and extracts the necessary information 202 | // to generate the payload. It generates the answers for the challenge using the `generateSecCptAnswers` 203 | // function and creates an ordered object containing the token and answers. 204 | // 205 | // Example usage: 206 | // 207 | // secCptCookie := "..." 208 | // payload, err := challenge.GenerateSecCptPayload(secCptCookie) 209 | // if err != nil { 210 | // // Handle the error 211 | // } 212 | // // Use the generated payload 213 | // fmt.Println(string(payload)) 214 | // 215 | // Parameters: 216 | // - secCptCookie: A string representing the value of the sec_cpt cookie. 217 | // 218 | // Returns: 219 | // - []byte: The generated payload as a byte slice. 220 | // - error: An error encountered during payload generation, or nil if generation was successful. 221 | // 222 | // Errors: 223 | // - errors.New("error parsing sec_cpt cookie"): Returned when the sec_cpt cookie is not in the expected format. 224 | // - Other errors may be returned by the underlying JSON marshaling. 225 | func (s *SecCptChallenge) GenerateSecCptPayload(secCptCookie string) ([]byte, error) { 226 | sec, _, found := strings.Cut(secCptCookie, "~") 227 | if !found { 228 | return nil, ErrSecCptInvalidCookie 229 | } 230 | 231 | answers := generateSecCptAnswers(sec, s.challengeData) 232 | 233 | payload := orderedobject.NewObject[any](2) 234 | payload.Set("token", s.challengeData.Token) 235 | payload.Set("answers", answers) 236 | 237 | return jsoniter.Marshal(payload) 238 | } 239 | 240 | // Sleep sleeps for the duration specified in the sec-cpt challenge. 241 | // 242 | // The function uses the `duration` field of the `SecCptChallenge` struct to determine 243 | // the number of seconds to sleep. It blocks the current goroutine for the specified duration. 244 | // 245 | // Example usage: 246 | // 247 | // challenge, err := ParseSecCptChallenge(html) 248 | // if err != nil { 249 | // // Handle the error 250 | // } 251 | // challenge.Sleep() 252 | // 253 | // Parameters: 254 | // - None 255 | // 256 | // Returns: 257 | // - None 258 | func (s *SecCptChallenge) Sleep() { 259 | time.Sleep(time.Second * time.Duration(s.duration)) 260 | } 261 | 262 | // SleepWithContext sleeps for the duration specified in the sec-cpt challenge or until the provided context is done. 263 | // 264 | // The function uses the `duration` field of the `SecCptChallenge` struct to determine 265 | // the number of seconds to sleep. It creates a timer with the specified duration and waits for either 266 | // the timer to expire or the provided context to be done. If the context is done before the timer expires, 267 | // the timer is stopped to prevent it from firing. 268 | // 269 | // Example usage: 270 | // 271 | // challenge, err := ParseSecCptChallenge(html) 272 | // if err != nil { 273 | // // Handle the error 274 | // } 275 | // ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 276 | // defer cancel() 277 | // challenge.SleepWithContext(ctx) 278 | // 279 | // Parameters: 280 | // - ctx: A context.Context that can be used to cancel the sleep operation. 281 | // 282 | // Returns: 283 | // - None 284 | func (s *SecCptChallenge) SleepWithContext(ctx context.Context) { 285 | timer := time.NewTimer(time.Second * time.Duration(s.duration)) 286 | select { 287 | case <-ctx.Done(): 288 | if !timer.Stop() { 289 | <-timer.C 290 | } 291 | case <-timer.C: 292 | } 293 | } 294 | 295 | func generateSecCptAnswers(sec string, challengeData *secCptChallengeData) []string { 296 | answers := make([]string, challengeData.Count) 297 | challenge := sec + strconv.Itoa(challengeData.Timestamp) + challengeData.Nonce 298 | hash := sha256.New() 299 | var hashBytes [sha256.Size]byte 300 | 301 | for i := range answers { 302 | initialPart := []byte(challenge + strconv.Itoa(challengeData.Difficulty+i)) 303 | 304 | buf := make([]byte, len(initialPart)+64) 305 | copy(buf, initialPart) 306 | 307 | for { 308 | answerLen := internal.FloatToStringRadix(rand.Float64(), 16, buf[len(initialPart):]) 309 | hash.Reset() 310 | hash.Write(buf[:len(initialPart)+answerLen]) 311 | hash.Sum(hashBytes[:0]) 312 | 313 | var output int 314 | for _, v := range hashBytes { 315 | output = int(int32(uint32((output<<8)|int(v)) >> 0)) 316 | output %= challengeData.Difficulty + i 317 | } 318 | 319 | if output != 0 { 320 | continue 321 | } 322 | 323 | answers[i] = string(buf[len(initialPart) : len(initialPart)+answerLen]) 324 | break 325 | } 326 | } 327 | 328 | return answers 329 | } 330 | -------------------------------------------------------------------------------- /akamai/sec_cpt_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package akamai 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai(in *jlexer.Lexer, out *secCptChallengeData) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeFieldName(false) 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "token": 40 | out.Token = string(in.String()) 41 | case "timestamp": 42 | out.Timestamp = int(in.Int()) 43 | case "nonce": 44 | out.Nonce = string(in.String()) 45 | case "difficulty": 46 | out.Difficulty = int(in.Int()) 47 | case "count": 48 | out.Count = int(in.Int()) 49 | case "timeout": 50 | out.Timeout = int(in.Int()) 51 | case "cpu": 52 | out.CPU = bool(in.Bool()) 53 | case "verify_url": 54 | out.VerifyURL = string(in.String()) 55 | default: 56 | in.SkipRecursive() 57 | } 58 | in.WantComma() 59 | } 60 | in.Delim('}') 61 | if isTopLevel { 62 | in.Consumed() 63 | } 64 | } 65 | func easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai(out *jwriter.Writer, in secCptChallengeData) { 66 | out.RawByte('{') 67 | first := true 68 | _ = first 69 | { 70 | const prefix string = ",\"token\":" 71 | out.RawString(prefix[1:]) 72 | out.String(string(in.Token)) 73 | } 74 | { 75 | const prefix string = ",\"timestamp\":" 76 | out.RawString(prefix) 77 | out.Int(int(in.Timestamp)) 78 | } 79 | { 80 | const prefix string = ",\"nonce\":" 81 | out.RawString(prefix) 82 | out.String(string(in.Nonce)) 83 | } 84 | { 85 | const prefix string = ",\"difficulty\":" 86 | out.RawString(prefix) 87 | out.Int(int(in.Difficulty)) 88 | } 89 | { 90 | const prefix string = ",\"count\":" 91 | out.RawString(prefix) 92 | out.Int(int(in.Count)) 93 | } 94 | { 95 | const prefix string = ",\"timeout\":" 96 | out.RawString(prefix) 97 | out.Int(int(in.Timeout)) 98 | } 99 | { 100 | const prefix string = ",\"cpu\":" 101 | out.RawString(prefix) 102 | out.Bool(bool(in.CPU)) 103 | } 104 | { 105 | const prefix string = ",\"verify_url\":" 106 | out.RawString(prefix) 107 | out.String(string(in.VerifyURL)) 108 | } 109 | out.RawByte('}') 110 | } 111 | 112 | // MarshalJSON supports json.Marshaler interface 113 | func (v secCptChallengeData) MarshalJSON() ([]byte, error) { 114 | w := jwriter.Writer{} 115 | easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai(&w, v) 116 | return w.Buffer.BuildBytes(), w.Error 117 | } 118 | 119 | // MarshalEasyJSON supports easyjson.Marshaler interface 120 | func (v secCptChallengeData) MarshalEasyJSON(w *jwriter.Writer) { 121 | easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai(w, v) 122 | } 123 | 124 | // UnmarshalJSON supports json.Unmarshaler interface 125 | func (v *secCptChallengeData) UnmarshalJSON(data []byte) error { 126 | r := jlexer.Lexer{Data: data} 127 | easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai(&r, v) 128 | return r.Error() 129 | } 130 | 131 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 132 | func (v *secCptChallengeData) UnmarshalEasyJSON(l *jlexer.Lexer) { 133 | easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai(l, v) 134 | } 135 | func easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai1(in *jlexer.Lexer, out *secCptApiResponse) { 136 | isTopLevel := in.IsStart() 137 | if in.IsNull() { 138 | if isTopLevel { 139 | in.Consumed() 140 | } 141 | in.Skip() 142 | return 143 | } 144 | in.Delim('{') 145 | for !in.IsDelim('}') { 146 | key := in.UnsafeFieldName(false) 147 | in.WantColon() 148 | if in.IsNull() { 149 | in.Skip() 150 | in.WantComma() 151 | continue 152 | } 153 | switch key { 154 | case "sec-cp-challenge": 155 | out.SecCpChallenge = string(in.String()) 156 | case "provider": 157 | out.Provider = string(in.String()) 158 | case "branding_url_content": 159 | out.BrandingURLContent = string(in.String()) 160 | case "chlg_duration": 161 | out.ChlgDuration = int(in.Int()) 162 | case "token": 163 | out.Token = string(in.String()) 164 | case "timestamp": 165 | out.Timestamp = int(in.Int()) 166 | case "nonce": 167 | out.Nonce = string(in.String()) 168 | case "difficulty": 169 | out.Difficulty = int(in.Int()) 170 | case "timeout": 171 | out.Timeout = int(in.Int()) 172 | case "cpu": 173 | out.CPU = bool(in.Bool()) 174 | default: 175 | in.SkipRecursive() 176 | } 177 | in.WantComma() 178 | } 179 | in.Delim('}') 180 | if isTopLevel { 181 | in.Consumed() 182 | } 183 | } 184 | func easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai1(out *jwriter.Writer, in secCptApiResponse) { 185 | out.RawByte('{') 186 | first := true 187 | _ = first 188 | { 189 | const prefix string = ",\"sec-cp-challenge\":" 190 | out.RawString(prefix[1:]) 191 | out.String(string(in.SecCpChallenge)) 192 | } 193 | { 194 | const prefix string = ",\"provider\":" 195 | out.RawString(prefix) 196 | out.String(string(in.Provider)) 197 | } 198 | { 199 | const prefix string = ",\"branding_url_content\":" 200 | out.RawString(prefix) 201 | out.String(string(in.BrandingURLContent)) 202 | } 203 | { 204 | const prefix string = ",\"chlg_duration\":" 205 | out.RawString(prefix) 206 | out.Int(int(in.ChlgDuration)) 207 | } 208 | { 209 | const prefix string = ",\"token\":" 210 | out.RawString(prefix) 211 | out.String(string(in.Token)) 212 | } 213 | { 214 | const prefix string = ",\"timestamp\":" 215 | out.RawString(prefix) 216 | out.Int(int(in.Timestamp)) 217 | } 218 | { 219 | const prefix string = ",\"nonce\":" 220 | out.RawString(prefix) 221 | out.String(string(in.Nonce)) 222 | } 223 | { 224 | const prefix string = ",\"difficulty\":" 225 | out.RawString(prefix) 226 | out.Int(int(in.Difficulty)) 227 | } 228 | { 229 | const prefix string = ",\"timeout\":" 230 | out.RawString(prefix) 231 | out.Int(int(in.Timeout)) 232 | } 233 | { 234 | const prefix string = ",\"cpu\":" 235 | out.RawString(prefix) 236 | out.Bool(bool(in.CPU)) 237 | } 238 | out.RawByte('}') 239 | } 240 | 241 | // MarshalJSON supports json.Marshaler interface 242 | func (v secCptApiResponse) MarshalJSON() ([]byte, error) { 243 | w := jwriter.Writer{} 244 | easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai1(&w, v) 245 | return w.Buffer.BuildBytes(), w.Error 246 | } 247 | 248 | // MarshalEasyJSON supports easyjson.Marshaler interface 249 | func (v secCptApiResponse) MarshalEasyJSON(w *jwriter.Writer) { 250 | easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai1(w, v) 251 | } 252 | 253 | // UnmarshalJSON supports json.Unmarshaler interface 254 | func (v *secCptApiResponse) UnmarshalJSON(data []byte) error { 255 | r := jlexer.Lexer{Data: data} 256 | easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai1(&r, v) 257 | return r.Error() 258 | } 259 | 260 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 261 | func (v *secCptApiResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { 262 | easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai1(l, v) 263 | } 264 | func easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai2(in *jlexer.Lexer, out *SecCptChallenge) { 265 | isTopLevel := in.IsStart() 266 | if in.IsNull() { 267 | if isTopLevel { 268 | in.Consumed() 269 | } 270 | in.Skip() 271 | return 272 | } 273 | in.Delim('{') 274 | for !in.IsDelim('}') { 275 | key := in.UnsafeFieldName(false) 276 | in.WantColon() 277 | if in.IsNull() { 278 | in.Skip() 279 | in.WantComma() 280 | continue 281 | } 282 | switch key { 283 | case "ChallengePath": 284 | out.ChallengePath = string(in.String()) 285 | default: 286 | in.SkipRecursive() 287 | } 288 | in.WantComma() 289 | } 290 | in.Delim('}') 291 | if isTopLevel { 292 | in.Consumed() 293 | } 294 | } 295 | func easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai2(out *jwriter.Writer, in SecCptChallenge) { 296 | out.RawByte('{') 297 | first := true 298 | _ = first 299 | { 300 | const prefix string = ",\"ChallengePath\":" 301 | out.RawString(prefix[1:]) 302 | out.String(string(in.ChallengePath)) 303 | } 304 | out.RawByte('}') 305 | } 306 | 307 | // MarshalJSON supports json.Marshaler interface 308 | func (v SecCptChallenge) MarshalJSON() ([]byte, error) { 309 | w := jwriter.Writer{} 310 | easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai2(&w, v) 311 | return w.Buffer.BuildBytes(), w.Error 312 | } 313 | 314 | // MarshalEasyJSON supports easyjson.Marshaler interface 315 | func (v SecCptChallenge) MarshalEasyJSON(w *jwriter.Writer) { 316 | easyjsonAb81e4ffEncodeGithubComHyperSolutionsHyperSdkGoAkamai2(w, v) 317 | } 318 | 319 | // UnmarshalJSON supports json.Unmarshaler interface 320 | func (v *SecCptChallenge) UnmarshalJSON(data []byte) error { 321 | r := jlexer.Lexer{Data: data} 322 | easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai2(&r, v) 323 | return r.Error() 324 | } 325 | 326 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 327 | func (v *SecCptChallenge) UnmarshalEasyJSON(l *jlexer.Lexer) { 328 | easyjsonAb81e4ffDecodeGithubComHyperSolutionsHyperSdkGoAkamai2(l, v) 329 | } 330 | -------------------------------------------------------------------------------- /akamai/sec_cpt_test.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | const secCptHtml = `` 10 | 11 | const secCptJson = `{"sec-cp-challenge":"true","provider":"crypto","branding_url_content":"/_sec/cp_challenge/crypto_message-4-3.htm","chlg_duration":30,"token":"AAQAAAAJ_____9z_ZPsdHbk36hg2f6np2sGJDXmkwGmBiMBr_DDEmSWfi8Zt7BdtjWrNd9KD4DS_vim0VnK2wsa8tIC7XWsCshkvDF9J9Rf5EFwBU00c6SMXTaSNSTcDR-HVFGp3uAa67Mb3I6HeifXbjALcEomjcnwa9ZNQdDWuTAUTgNGbYw09A8AXIuP9DNv3QktUx488FV38Rm6xBXr66-MmD05hsBhucIYpLS_VCJVs9OFPnWsksPJ19ibw2K3fabfJbzIdB3Xv3J0kzLQ0gY7bpLRXK1oAcUTxNNsy-LQGe_lyV6INQ4ojPLGJpOTk","timestamp":1713283747,"nonce":"ebccdb479fcb92636fbc","difficulty":15000,"timeout":1000,"cpu":false}` 12 | 13 | func TestParseSecCptChallenge(t *testing.T) { 14 | input := strings.NewReader(secCptHtml) 15 | 16 | challenge, err := ParseSecCptChallenge(input) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | 21 | if challenge.duration != 5 { 22 | t.Errorf("Expected duration to be 5, got %d", challenge.duration) 23 | } 24 | 25 | if challenge.challengeData.Difficulty != 2500 { 26 | t.Errorf("Expected difficulty to be 2500, got %d", challenge.challengeData.Difficulty) 27 | } 28 | 29 | if challenge.ChallengePath != "/_sec/cp_challenge/ak-challenge-4-3.htm" { 30 | t.Errorf("Expected challenge path to be '/_sec/cp_challenge/ak-challenge-4-3.htm', got %s", challenge.ChallengePath) 31 | } 32 | } 33 | 34 | func TestParseSecCptChallengeData(t *testing.T) { 35 | input := strings.NewReader(secCptHtml) 36 | src, _ := io.ReadAll(input) 37 | 38 | challengeData, err := parseSecCptChallengeData(src) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | 43 | if challengeData.Token != "AAQAAAAJ_____8qtEZNLUTePXrEsHuMA6qtfNwh0aF4y0BXhd6NRuvTEtjSsZE5nckh_H4_HP9nnlwEz84XYA-PiErO4BcCzW9Jey7YLrFbcN5H7O_DbpQJXJ9s46LrrbTBd0XuTUpCXXRmrZwblW5fF-mxQAWHieNi6IFucTSNLzlYgmJRpBLXdGRpG9KYAu9cWU4dSuCj2-ICFQch2-CP_pZG1eUbgw5D-1J4OtKo9GBK5cLamjHCGAe5Suii-mLLbaioYgn6K4VgsZmOyZp2G6xqWiN7yfGDIy5xDmzlquyBaG5YAPLLpaiosmXzvPkM" { 44 | t.Errorf("Expected token to be 'AAQAAAAJ_____8qtEZNLUTePXrEsHuMA6qtfNwh0aF4y0BXhd6NRuvTEtjSsZE5nckh_H4_HP9nnlwEz84XYA-PiErO4BcCzW9Jey7YLrFbcN5H7O_DbpQJXJ9s46LrrbTBd0XuTUpCXXRmrZwblW5fF-mxQAWHieNi6IFucTSNLzlYgmJRpBLXdGRpG9KYAu9cWU4dSuCj2-ICFQch2-CP_pZG1eUbgw5D-1J4OtKo9GBK5cLamjHCGAe5Suii-mLLbaioYgn6K4VgsZmOyZp2G6xqWiN7yfGDIy5xDmzlquyBaG5YAPLLpaiosmXzvPkM', got %s", challengeData.Token) 45 | } 46 | 47 | if challengeData.Timestamp != 1712237549 { 48 | t.Errorf("Expected timestamp to be 1712237549, got %d", challengeData.Timestamp) 49 | } 50 | 51 | if challengeData.Nonce != "ab3028d518738bd8faad" { 52 | t.Errorf("Expected nonce to be 'ab3028d518738bd8faad', got %s", challengeData.Nonce) 53 | } 54 | 55 | if challengeData.Difficulty != 2500 { 56 | t.Errorf("Expected difficulty to be 2500, got %d", challengeData.Difficulty) 57 | } 58 | 59 | if challengeData.Timeout != 1000 { 60 | t.Errorf("Expected timeout to be 1000, got %d", challengeData.Timeout) 61 | } 62 | } 63 | 64 | func TestParseSecCptChallengeFromJson(t *testing.T) { 65 | challenge, err := ParseSecCptChallengeFromJson(strings.NewReader(secCptJson)) 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | 70 | if challenge.challengeData.Token != "AAQAAAAJ_____9z_ZPsdHbk36hg2f6np2sGJDXmkwGmBiMBr_DDEmSWfi8Zt7BdtjWrNd9KD4DS_vim0VnK2wsa8tIC7XWsCshkvDF9J9Rf5EFwBU00c6SMXTaSNSTcDR-HVFGp3uAa67Mb3I6HeifXbjALcEomjcnwa9ZNQdDWuTAUTgNGbYw09A8AXIuP9DNv3QktUx488FV38Rm6xBXr66-MmD05hsBhucIYpLS_VCJVs9OFPnWsksPJ19ibw2K3fabfJbzIdB3Xv3J0kzLQ0gY7bpLRXK1oAcUTxNNsy-LQGe_lyV6INQ4ojPLGJpOTk" { 71 | t.Fail() 72 | } 73 | 74 | if challenge.challengeData.Timestamp != 1713283747 { 75 | t.Fail() 76 | } 77 | 78 | if challenge.challengeData.Nonce != "ebccdb479fcb92636fbc" { 79 | t.Fail() 80 | } 81 | 82 | if challenge.challengeData.Difficulty != 15000 { 83 | t.Fail() 84 | } 85 | 86 | if challenge.challengeData.Timeout != 1000 { 87 | t.Fail() 88 | } 89 | } 90 | 91 | func BenchmarkParseSecCptChallenge(b *testing.B) { 92 | b.ReportAllocs() 93 | for i := 0; i < b.N; i++ { 94 | if _, err := ParseSecCptChallenge(strings.NewReader(secCptHtml)); err != nil { 95 | b.Error(err) 96 | } 97 | } 98 | } 99 | 100 | func BenchmarkSecCptChallenge_GenerateSecCptPayload(b *testing.B) { 101 | input := strings.NewReader(secCptHtml) 102 | 103 | challenge, err := ParseSecCptChallenge(input) 104 | if err != nil { 105 | b.Fatal(err) 106 | } 107 | const cookie = `3F3B2D3E2ABE67693EE8134E57C501C8~...` 108 | 109 | b.ReportAllocs() 110 | for i := 0; i < b.N; i++ { 111 | if _, err := challenge.GenerateSecCptPayload(cookie); err != nil { 112 | b.Error(err) 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /akamai/stop_signal.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | // IsCookieValid determines if the provided _abck cookie value is valid, based on Akamai Bot Manager's 9 | // client-side stop signal mechanism using the given request count. If the result is true, the client is ADVISED 10 | // to halt further sensor data submissions. Submitting further would still produce a valid cookie but is unnecessary. 11 | // 12 | // The stop signal mechanism in the Akamai Bot Manager's client-side script informs a client that the cookie received is 13 | // valid and that any additional submissions are superfluous. 14 | // 15 | // However, some applications do not activate the stop signal feature. In such scenarios, the client will continue 16 | // submitting data whenever a trigger event occurs. Under these circumstances, verifying the authenticity of a cookie 17 | // without sending it to a secured endpoint becomes challenging. 18 | func IsCookieValid(cookie string, requestCount int) bool { 19 | parts := strings.Split(cookie, "~") 20 | if len(parts) < 2 { 21 | return false 22 | } 23 | 24 | requestThreshold, err := strconv.Atoi(parts[1]) 25 | if err != nil { 26 | requestThreshold = -1 27 | } 28 | 29 | return requestThreshold != -1 && requestCount >= requestThreshold 30 | } 31 | 32 | // IsCookieInvalidated determines if the current session requires more sensors to be sent. 33 | // 34 | // Protected endpoints can invalidate a session by setting a new _abck cookie that ends in '~0~-1~-1' or similar. 35 | // This function returns if such an invalidated cookie is present, if it is present you should be able to make the 36 | // cookie valid again with only 1 sensor post. 37 | func IsCookieInvalidated(cookie string) bool { 38 | parts := strings.Split(cookie, "~") 39 | if len(parts) < 4 { 40 | return false 41 | } 42 | 43 | signal, err := strconv.Atoi(parts[3]) 44 | if err != nil { 45 | signal = -1 46 | } 47 | 48 | return signal > -1 49 | } 50 | -------------------------------------------------------------------------------- /akamai/stop_signal_test.go: -------------------------------------------------------------------------------- 1 | package akamai 2 | 3 | import "testing" 4 | 5 | func TestIsCookieValid(t *testing.T) { 6 | const ( 7 | validCookie = `0C8A2251CC04F60F59160D6AD92DA8A0~0~YAAQlivJF6o1GjGGAQAAaNihYgldsErwKa3aAlB+oRlgZYviinJa+Q29XMXmkwJUNCgQPooQUyhfjhAgavSMACfCk1doYnUa4dYmsVUWbWB+QGFEPuwcvLVQscLV8taHWIFuFxb94vEJ8MnSY9sQlhRN9i2iNgZ5QJz8h2s2mMm4ZO+i890DPaHfJPkSrYtc9ivgbjDA/jFpK6k2Pq8Pu25dCI55zOqOeSyaChgtJyF6KvlnlyVrqOa12tThX+prb52et7FRGmqhw8LU5X1E07WShiKDmJw2Rb8+odHcA28bD3EITXTy43QFb2PKR9Q3jy57KFEQYFaeW4xfAe8BxjdpkYt5vSH50nmbi1SXOWIxL4QV0b8psJgMCIq+ZMR+ZBU2opuHgxAucktvbIffGuJPWFYJu8thjxr5HGBtZBUqc6LwccFQI+DZd+hpZfsRJscvNx0yWmiPN8/gJiVLGkjHWYL5xmVaVCYceTtGL+7N~-1~-1~-1` 8 | 9 | invalidCookie = `3B508597CDC152514C3D85CF2749455C~-1~YAAQNcMTAgMgNEyKAQAAaxaxTwrFBKU37u+fFlXF8NMV3M3n4A2lNyvNTV14HhbYAyJWQdsaUYXmjc/7GGsPQ8EYyPjUZr6X8guTu9q9mZY2ZeF5IWdB2jRHLVzEttloRl8RMGS+dP34QSaMYx98elcgQchq+DAiRDB1XoeKDzwdZnxhfLRu8vAilIaR+i/NPf1Y1fR+n85SIp5OlpMJYK71eoL/D9wZTbpZQVHbSP4rhHG4wNaHNkuo5KkwWpgbQfLWIwhatuhn/xZ1mJEQVnZwhHg9aDF+ooxiPm+2+HK/QI5zSGZ8zz9CMZ9PI+jegXtqznth6eBXJ7ZXCr5VfgjnepKpJW3WJfKv53dHHWUtkuBEOTozjuR1RYcsFo07oNmUqWBhQ0EslRCJpMAn7ehwuyC2w7BRlrZuSvDOAcxUgbgN2LSBEA==~-1~-1~-1` 10 | ) 11 | 12 | if !IsCookieValid(validCookie, 1) { 13 | t.Fail() 14 | } 15 | 16 | if IsCookieValid(invalidCookie, 1) { 17 | t.Fail() 18 | } 19 | } 20 | 21 | func BenchmarkIsCookieValid(b *testing.B) { 22 | const cookie = `0C8A2251CC04F60F59160D6AD92DA8A0~0~YAAQlivJF6o1GjGGAQAAaNihYgldsErwKa3aAlB+oRlgZYviinJa+Q29XMXmkwJUNCgQPooQUyhfjhAgavSMACfCk1doYnUa4dYmsVUWbWB+QGFEPuwcvLVQscLV8taHWIFuFxb94vEJ8MnSY9sQlhRN9i2iNgZ5QJz8h2s2mMm4ZO+i890DPaHfJPkSrYtc9ivgbjDA/jFpK6k2Pq8Pu25dCI55zOqOeSyaChgtJyF6KvlnlyVrqOa12tThX+prb52et7FRGmqhw8LU5X1E07WShiKDmJw2Rb8+odHcA28bD3EITXTy43QFb2PKR9Q3jy57KFEQYFaeW4xfAe8BxjdpkYt5vSH50nmbi1SXOWIxL4QV0b8psJgMCIq+ZMR+ZBU2opuHgxAucktvbIffGuJPWFYJu8thjxr5HGBtZBUqc6LwccFQI+DZd+hpZfsRJscvNx0yWmiPN8/gJiVLGkjHWYL5xmVaVCYceTtGL+7N~-1~-1~-1` 23 | 24 | b.ReportAllocs() 25 | for i := 0; i < b.N; i++ { 26 | IsCookieValid(cookie, 1) 27 | } 28 | } 29 | 30 | func TestIsCookieInvalidated(t *testing.T) { 31 | const ( 32 | invalidatedCookie = `7D715603CF98EF4593FB1BABFA0BA525~-1~YAAQ5oFlX6qoLeeGAQAAi7B99gl3E4KSKtdW8AEn7RB8D1CpzSqsnYR5E24Q66mWSu8yXMOVdmjYPVFVad6QNKZ/w6xs2sU9sX/t4GNgLFqNLn3Qcags4msWUL6Mdlmh/MKPZWiBnU6pGnAec9cdYW9gAWiZ3kSvCxJHYD536EBIJKKkZ/EcCCKauQbnn+TUuSp4D2jSQfUEOaXMTiSREKRnqLpc9lmgG8hkFBeeyWlu7vv+iussTelN6o5zCHwH16ztaLQTDVRclRGaUo2jqN7dpDd8V0WvZ7NnbNsiU2Ac52TBM7Kjl5/l2ltAAlYr+vgnc3QRhbCo8trn2RrEP7nkCRF1RzQ3HvG097nul3hcRPXitfIslgVG9ur67LTwpRt58DqjgjNz4qHR5R77VzyTUQPt8ZQzMeh4s9TOr/E=~0~-1~-1` 33 | ) 34 | 35 | if !IsCookieInvalidated(invalidatedCookie) { 36 | t.Fail() 37 | } 38 | } 39 | 40 | func BenchmarkIsCookieInvalidated(b *testing.B) { 41 | const cookie = `0C8A2251CC04F60F59160D6AD92DA8A0~0~YAAQlivJF6o1GjGGAQAAaNihYgldsErwKa3aAlB+oRlgZYviinJa+Q29XMXmkwJUNCgQPooQUyhfjhAgavSMACfCk1doYnUa4dYmsVUWbWB+QGFEPuwcvLVQscLV8taHWIFuFxb94vEJ8MnSY9sQlhRN9i2iNgZ5QJz8h2s2mMm4ZO+i890DPaHfJPkSrYtc9ivgbjDA/jFpK6k2Pq8Pu25dCI55zOqOeSyaChgtJyF6KvlnlyVrqOa12tThX+prb52et7FRGmqhw8LU5X1E07WShiKDmJw2Rb8+odHcA28bD3EITXTy43QFb2PKR9Q3jy57KFEQYFaeW4xfAe8BxjdpkYt5vSH50nmbi1SXOWIxL4QV0b8psJgMCIq+ZMR+ZBU2opuHgxAucktvbIffGuJPWFYJu8thjxr5HGBtZBUqc6LwccFQI+DZd+hpZfsRJscvNx0yWmiPN8/gJiVLGkjHWYL5xmVaVCYceTtGL+7N~-1~-1~-1` 42 | 43 | b.ReportAllocs() 44 | for i := 0; i < b.N; i++ { 45 | IsCookieInvalidated(cookie) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package hyper 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "errors" 7 | "fmt" 8 | "github.com/Hyper-Solutions/hyper-sdk-go/v2/internal" 9 | jsoniter "github.com/json-iterator/go" 10 | "github.com/mailru/easyjson" 11 | "github.com/mailru/easyjson/buffer" 12 | "github.com/mailru/easyjson/jwriter" 13 | "net/http" 14 | ) 15 | 16 | func sendRequest[V easyjson.Marshaler, T easyjson.Unmarshaler](ctx context.Context, s *Session, url string, input V) (response T, err error) { 17 | if s.ApiKey == "" { 18 | return response, errors.New("missing api key") 19 | } 20 | 21 | w := jwriter.Writer{ 22 | Flags: 0, 23 | Error: nil, 24 | Buffer: buffer.Buffer{}, 25 | NoEscapeHTML: true, 26 | } 27 | 28 | input.MarshalEasyJSON(&w) 29 | 30 | if w.Error != nil { 31 | return response, w.Error 32 | } 33 | payload := w.Buffer.BuildBytes() 34 | 35 | useCompression := false 36 | 37 | if len(payload) > 1000 { 38 | compressedBody, err := internal.CompressZstd(payload) 39 | if err != nil { 40 | return response, fmt.Errorf("failed to compress request body with zstd: %w", err) 41 | } 42 | payload = compressedBody 43 | useCompression = true 44 | } 45 | 46 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(payload)) 47 | if err != nil { 48 | return response, err 49 | } 50 | req.Header.Set("content-type", "application/json") 51 | req.Header.Set("accept-encoding", "zstd") 52 | req.Header.Set("x-api-key", s.ApiKey) 53 | 54 | if useCompression { 55 | req.Header.Set("content-encoding", "zstd") 56 | } 57 | 58 | if s.JwtKey != nil { 59 | signature, err := s.generateSignature() 60 | if err != nil { 61 | return response, err 62 | } 63 | req.Header.Set("x-signature", signature) 64 | } 65 | 66 | resp, err := s.Client.Do(req) 67 | if err != nil { 68 | return response, err 69 | } 70 | defer resp.Body.Close() 71 | 72 | respBody, err := internal.DecompressResponse(resp) 73 | if err != nil { 74 | return response, err 75 | } 76 | 77 | if err := jsoniter.Unmarshal(respBody, &response); err != nil { 78 | return response, err 79 | } 80 | 81 | return response, nil 82 | } 83 | -------------------------------------------------------------------------------- /datadome.go: -------------------------------------------------------------------------------- 1 | package hyper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | // GenerateDataDomeSlider returns the URL that will return a solved datadome cookie when blocked by captcha, and 9 | // the extra sec-ch-* headers used on consequent requests. 10 | func (s *Session) GenerateDataDomeSlider(ctx context.Context, input *DataDomeSliderInput) (string, *Headers, error) { 11 | response, err := sendRequest[*DataDomeSliderInput, *apiResponse](ctx, s, "https://datadome.hypersolutions.co/slider", input) 12 | if err != nil { 13 | return "", nil, err 14 | } 15 | 16 | if response.Error != "" { 17 | return "", nil, fmt.Errorf("api returned with: %s", response.Error) 18 | } 19 | 20 | return response.Payload, response.Headers, nil 21 | } 22 | 23 | // GenerateDataDomeInterstitial returns the form data string that is used in the POST request to receive a solved datadome cookie, and 24 | // the extra sec-ch-* headers used on consequent requests. 25 | func (s *Session) GenerateDataDomeInterstitial(ctx context.Context, input *DataDomeInterstitialInput) (string, *Headers, error) { 26 | response, err := sendRequest[*DataDomeInterstitialInput, *apiResponse](ctx, s, "https://datadome.hypersolutions.co/interstitial", input) 27 | if err != nil { 28 | return "", nil, err 29 | } 30 | 31 | if response.Error != "" { 32 | return "", nil, fmt.Errorf("api returned with: %s", response.Error) 33 | } 34 | 35 | return response.Payload, response.Headers, nil 36 | } 37 | 38 | // GenerateDataDomeTags returns the tags data string that is used in the POST request to receive a solved datadome cookie. 39 | func (s *Session) GenerateDataDomeTags(ctx context.Context, input *DataDomeTagsInput) (string, error) { 40 | response, err := sendRequest[*DataDomeTagsInput, *apiResponse](ctx, s, "https://datadome.hypersolutions.co/tags", input) 41 | if err != nil { 42 | return "", err 43 | } 44 | 45 | if response.Error != "" { 46 | return "", fmt.Errorf("api returned with: %s", response.Error) 47 | } 48 | 49 | return response.Payload, nil 50 | } 51 | -------------------------------------------------------------------------------- /datadome/parse.go: -------------------------------------------------------------------------------- 1 | package datadome 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | jsoniter "github.com/json-iterator/go" 7 | "github.com/justhyped/OrderedForm" 8 | "io" 9 | "regexp" 10 | "strconv" 11 | ) 12 | 13 | var ( 14 | ddRegex = regexp.MustCompile(`var\s+dd\s*=\s*\{\s*([\s\S]*?)\s*}`) 15 | singleQuoteRegex = regexp.MustCompile(`'([^']*)'`) 16 | keyRegex = regexp.MustCompile(`([^"]|^)(\b\w+)\s*: `) 17 | ) 18 | 19 | // ParseInterstitialDeviceCheckLink parses the device check url (/interstitial/?initialCid...) from a blocked response body 20 | // 21 | // the datadome cookie is the current value of the 'datadome' cookie 22 | func ParseInterstitialDeviceCheckLink(body io.Reader, datadomeCookie, referer string) (string, error) { 23 | bodyBytes, err := io.ReadAll(body) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | matches := ddRegex.FindSubmatch(bodyBytes) 29 | if matches == nil || len(matches) < 2 { 30 | return "", errors.New("DD object not found") 31 | } 32 | 33 | jsonObject := append([]byte("{"), bytes.TrimSpace(matches[1])...) 34 | jsonObject = append(jsonObject, []byte("}")...) 35 | jsonObject = singleQuoteRegex.ReplaceAll(jsonObject, []byte(`"$1"`)) 36 | jsonObject = keyRegex.ReplaceAll(jsonObject, []byte(`$1"$2":`)) 37 | 38 | var d dd 39 | err = jsoniter.Unmarshal(jsonObject, &d) 40 | if err != nil { 41 | return "", err 42 | } 43 | 44 | form := new(OrderedForm.OrderedForm) 45 | form.Set("initialCid", d.Cid) 46 | form.Set("hash", d.Hsh) 47 | form.Set("cid", datadomeCookie) 48 | form.Set("referer", referer) 49 | form.Set("s", strconv.FormatInt(d.S, 10)) 50 | form.Set("b", strconv.FormatInt(d.B, 10)) 51 | form.Set("dm", "cd") 52 | 53 | return "https://geo.captcha-delivery.com/interstitial/?" + form.URLEncode(), nil 54 | } 55 | 56 | // ParseSliderDeviceCheckLink parses the device check url (/captcha/?initialCid...) from a blocked response body 57 | // 58 | // the datadome cookie is the current value of the 'datadome' cookie 59 | func ParseSliderDeviceCheckLink(body io.Reader, datadomeCookie, referer string) (string, error) { 60 | bodyBytes, err := io.ReadAll(body) 61 | if err != nil { 62 | return "", err 63 | } 64 | 65 | matches := ddRegex.FindSubmatch(bodyBytes) 66 | if matches == nil || len(matches) < 2 { 67 | return "", errors.New("DD object not found") 68 | } 69 | 70 | jsonObject := append([]byte("{"), bytes.TrimSpace(matches[1])...) 71 | jsonObject = append(jsonObject, []byte("}")...) 72 | 73 | jsonObject = singleQuoteRegex.ReplaceAll(jsonObject, []byte(`"$1"`)) 74 | jsonObject = keyRegex.ReplaceAll(jsonObject, []byte(`$1"$2":`)) 75 | 76 | var d dd 77 | err = jsoniter.Unmarshal(jsonObject, &d) 78 | if err != nil { 79 | return "", err 80 | } 81 | 82 | if d.T == "bv" { 83 | return "", errors.New("proxy blocked") 84 | } 85 | 86 | form := new(OrderedForm.OrderedForm) 87 | form.Set("initialCid", d.Cid) 88 | form.Set("hash", d.Hsh) 89 | form.Set("cid", datadomeCookie) 90 | form.Set("t", d.T) 91 | form.Set("referer", referer) 92 | form.Set("s", strconv.FormatInt(d.S, 10)) 93 | form.Set("e", d.E) 94 | form.Set("dm", "cd") 95 | 96 | return "https://geo.captcha-delivery.com/captcha/?" + form.URLEncode(), nil 97 | } 98 | 99 | type dd struct { 100 | Rt string `json:"rt"` 101 | Cid string `json:"cid"` 102 | Hsh string `json:"hsh"` 103 | B int64 `json:"b"` 104 | S int64 `json:"s"` 105 | E string `json:"e"` 106 | T string `json:"t"` 107 | } 108 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Hyper-Solutions/hyper-sdk-go/v2 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/Hyper-Solutions/orderedobject v1.0.0 7 | github.com/andybalholm/brotli v1.1.1 8 | github.com/golang-jwt/jwt/v5 v5.2.2 9 | github.com/json-iterator/go v1.1.12 10 | github.com/justhyped/OrderedForm v0.0.0-20230202094228-bc7aa3c135e8 11 | github.com/klauspost/compress v1.18.0 12 | github.com/mailru/easyjson v0.9.0 13 | ) 14 | 15 | require ( 16 | github.com/josharian/intern v1.0.0 // indirect 17 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 18 | github.com/modern-go/reflect2 v1.0.2 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Hyper-Solutions/orderedobject v1.0.0 h1:y91n3VD0fIzv3Zj9OA8ACqTJS+v8EkoTj3VEe8cZWwA= 2 | github.com/Hyper-Solutions/orderedobject v1.0.0/go.mod h1:zv+R9tKJzKHsmriExjZbU2e0TT6fcXk+GE5QrrFln6Y= 3 | github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= 4 | github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= 9 | github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 10 | github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 11 | github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 12 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 13 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 14 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 15 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 16 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 17 | github.com/justhyped/OrderedForm v0.0.0-20230202094228-bc7aa3c135e8 h1:dlQIJuSXTHJNoWxyq1+/dWr+WZv/Wj+x0v11oCszeoU= 18 | github.com/justhyped/OrderedForm v0.0.0-20230202094228-bc7aa3c135e8/go.mod h1:dhyuQuEIjVZRWCyZmq2BVKzXFLdMiidE7yRX8BTlqmw= 19 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 20 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 21 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 22 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 23 | github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= 24 | github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= 25 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 26 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 27 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 28 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 29 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 30 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 31 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 32 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 33 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 34 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 35 | github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= 36 | github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= 37 | -------------------------------------------------------------------------------- /incapsula.go: -------------------------------------------------------------------------------- 1 | package hyper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/url" 7 | ) 8 | 9 | // GenerateReese84Sensor returns the sensor data required to generate valid reese84 cookies using the Hyper Solutions API. 10 | func (s *Session) GenerateReese84Sensor(ctx context.Context, site string, input *ReeseInput) (string, error) { 11 | response, err := sendRequest[*ReeseInput, *apiResponse](ctx, s, "https://incapsula.hypersolutions.co/reese84/"+url.PathEscape(site), input) 12 | if err != nil { 13 | return "", err 14 | } 15 | if response.Error != "" { 16 | return "", fmt.Errorf("api returned with: %s", response.Error) 17 | } 18 | 19 | return response.Payload, nil 20 | } 21 | 22 | // GenerateUtmvcCookie returns the utmvc cookie using the Hyper Solutions API. 23 | func (s *Session) GenerateUtmvcCookie(ctx context.Context, input *UtmvcInput) (string, string, error) { 24 | response, err := sendRequest[*UtmvcInput, *apiResponse](ctx, s, "https://incapsula.hypersolutions.co/utmvc", input) 25 | if err != nil { 26 | return "", "", err 27 | } 28 | if response.Error != "" { 29 | return "", "", fmt.Errorf("api returned with: %s", response.Error) 30 | } 31 | 32 | return response.Payload, response.Swhanedl, nil 33 | } 34 | -------------------------------------------------------------------------------- /incapsula/dynamic.go: -------------------------------------------------------------------------------- 1 | package incapsula 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net/url" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | reeseScriptRegex = regexp.MustCompile(`src\s*=\s*"((/[^/]+/\d+)(?:\?.*)?)"`) 13 | 14 | ErrReeseScriptNotFound = errors.New("hyper: reese script not found") 15 | ErrNotInterruptionPage = errors.New("hyper: not an interruption page") 16 | ErrInvalidURL = errors.New("hyper: invalid URL") 17 | ) 18 | 19 | // ParseDynamicReeseScript parses the sensor path and script path from the given HTML content. 20 | // 21 | // This function searches the provided HTML for a script element containing a specific pattern 22 | // and extracts both the sensor path (shortened path) and script path (the full path). 23 | // It requires that the HTML contains "Pardon Our Interruption" to confirm it's the correct page type. 24 | // It also takes a URL string, extracts the hostname, and appends it to the sensor path. 25 | // Returns the sensor path (with hostname) and script path if found, or appropriate errors otherwise. 26 | func ParseDynamicReeseScript(html io.Reader, urlStr string) (sensorPath string, scriptPath string, err error) { 27 | // Parse the URL to extract hostname 28 | parsedURL, err := url.Parse(urlStr) 29 | if err != nil { 30 | return "", "", ErrInvalidURL 31 | } 32 | hostname := parsedURL.Hostname() 33 | 34 | bytes, err := io.ReadAll(html) 35 | if err != nil { 36 | return "", "", err 37 | } 38 | 39 | content := string(bytes) 40 | 41 | // Verify this is an interruption page 42 | if !strings.Contains(content, "Pardon Our Interruption") { 43 | return "", "", ErrNotInterruptionPage 44 | } 45 | 46 | matches := reeseScriptRegex.FindStringSubmatch(content) 47 | if len(matches) < 3 { 48 | return "", "", ErrReeseScriptNotFound 49 | } 50 | 51 | scriptPath = matches[1] 52 | sensorPath = matches[2] 53 | 54 | // Append the hostname to the sensor path 55 | return sensorPath + "?d=" + hostname, scriptPath, nil 56 | } 57 | -------------------------------------------------------------------------------- /incapsula/dynamic_test.go: -------------------------------------------------------------------------------- 1 | package incapsula 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestParseDynamicReeseScript(t *testing.T) { 9 | file, err := os.Open("../scripts/reese.html") 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | defer file.Close() 14 | 15 | sensorPath, scriptPath, err := ParseDynamicReeseScript(file, "https://www.smythstoys.com/") 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | 20 | t.Log(sensorPath, scriptPath) 21 | } 22 | -------------------------------------------------------------------------------- /incapsula/utmvc.go: -------------------------------------------------------------------------------- 1 | package incapsula 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "math/rand" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | var ( 12 | scriptRegex = regexp.MustCompile(`src="(/_Incapsula_Resource\?[^"]*)"`) 13 | 14 | ErrScriptNotFound = errors.New("hyper: utmvc script not found") 15 | ) 16 | 17 | // ParseUtmvcScriptPath parses the UTMVC script path from the given script content. 18 | // 19 | // This function searches the provided script content for a specific pattern matching the UTMVC script path 20 | // using a precompiled regular expression. It extracts and returns the first match if found. 21 | func ParseUtmvcScriptPath(script io.Reader) (string, error) { 22 | bytes, err := io.ReadAll(script) 23 | if err != nil { 24 | return "", err 25 | } 26 | 27 | match := scriptRegex.FindSubmatch(bytes) 28 | if len(match) < 2 { 29 | return "", ErrScriptNotFound 30 | } 31 | 32 | return string(match[1]), nil 33 | } 34 | 35 | // GetUtmvcSubmitPath generates a UTMVC submit path with a unique random query parameter. 36 | // 37 | // This function constructs a submit path for the UTMVC script by appending a random floating-point number as a query 38 | // parameter. The random number is used to ensure the uniqueness of the request. 39 | func GetUtmvcSubmitPath() string { 40 | return "/_Incapsula_Resource?SWKMTFSR=1&e=" + strconv.FormatFloat(rand.Float64(), 'g', -1, 64) 41 | } 42 | -------------------------------------------------------------------------------- /incapsula/utmvc_test.go: -------------------------------------------------------------------------------- 1 | package incapsula 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestParseUtmvcScriptPath(t *testing.T) { 9 | file, err := os.Open("../scripts/utmvc.html") 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | 14 | scriptPath, err := ParseUtmvcScriptPath(file) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | 19 | t.Log(scriptPath) 20 | } 21 | -------------------------------------------------------------------------------- /internal/decompress.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "github.com/andybalholm/brotli" 6 | "github.com/klauspost/compress/flate" 7 | "github.com/klauspost/compress/gzip" 8 | "github.com/klauspost/compress/zlib" 9 | "github.com/klauspost/compress/zstd" 10 | "io" 11 | "net/http" 12 | "strings" 13 | "sync" 14 | ) 15 | 16 | // Decoder pools 17 | var ( 18 | zstdDecoderPool = sync.Pool{ 19 | New: func() interface{} { 20 | decoder, err := zstd.NewReader(nil) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return decoder 25 | }, 26 | } 27 | 28 | gzipDecoderPool = sync.Pool{ 29 | New: func() interface{} { 30 | return new(gzip.Reader) 31 | }, 32 | } 33 | ) 34 | 35 | // DecompressResponse decompresses the response body based on Content-Encoding header 36 | func DecompressResponse(resp *http.Response) ([]byte, error) { 37 | if resp == nil || resp.Body == nil { 38 | return nil, nil 39 | } 40 | 41 | encoding := strings.ToLower(resp.Header.Get("Content-Encoding")) 42 | if encoding == "" { 43 | return io.ReadAll(resp.Body) 44 | } 45 | 46 | switch { 47 | case strings.Contains(encoding, "gzip"): 48 | return handleGzip(resp.Body) 49 | case strings.Contains(encoding, "zstd"): 50 | return handleZstd(resp.Body) 51 | case strings.Contains(encoding, "br"): 52 | return handleBrotli(resp.Body) 53 | case strings.Contains(encoding, "deflate"): 54 | return handleDeflate(resp.Body) 55 | default: 56 | // Unknown encoding, try to read as-is 57 | return io.ReadAll(resp.Body) 58 | } 59 | } 60 | 61 | func handleGzip(body io.ReadCloser) ([]byte, error) { 62 | reader := gzipDecoderPool.Get().(*gzip.Reader) 63 | defer gzipDecoderPool.Put(reader) 64 | 65 | if err := reader.Reset(body); err != nil { 66 | return nil, err 67 | } 68 | defer reader.Close() 69 | 70 | return io.ReadAll(reader) 71 | } 72 | 73 | func handleZstd(body io.ReadCloser) ([]byte, error) { 74 | decoder := zstdDecoderPool.Get().(*zstd.Decoder) 75 | defer zstdDecoderPool.Put(decoder) 76 | 77 | decoder.Reset(body) 78 | return io.ReadAll(decoder) 79 | } 80 | 81 | func handleBrotli(body io.ReadCloser) ([]byte, error) { 82 | brReader := brotli.NewReader(body) 83 | return io.ReadAll(brReader) 84 | } 85 | 86 | func handleDeflate(body io.ReadCloser) ([]byte, error) { 87 | // Read the entire compressed body first 88 | compressedData, err := io.ReadAll(body) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | // First try with zlib (RFC 1950) which is the correct format for HTTP "deflate" 94 | zlibReader, err := zlib.NewReader(bytes.NewReader(compressedData)) 95 | if err == nil { 96 | defer zlibReader.Close() 97 | return io.ReadAll(zlibReader) 98 | } 99 | 100 | // If zlib fails, try raw deflate as a fallback (RFC 1951) 101 | // Some servers incorrectly send raw deflate data without the zlib wrapper 102 | rawReader := flate.NewReader(bytes.NewReader(compressedData)) 103 | defer rawReader.Close() 104 | return io.ReadAll(rawReader) 105 | } 106 | -------------------------------------------------------------------------------- /internal/zstd.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/klauspost/compress/zstd" 7 | "sync" 8 | ) 9 | 10 | var zstdEncoderPool = sync.Pool{ 11 | New: func() interface{} { 12 | encoder, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedDefault)) 13 | if err != nil { 14 | panic(fmt.Sprintf("failed to create zstd encoder: %v", err)) 15 | } 16 | return encoder 17 | }, 18 | } 19 | 20 | func CompressZstd(data []byte) ([]byte, error) { 21 | encoderInterface := zstdEncoderPool.Get() 22 | encoder := encoderInterface.(*zstd.Encoder) 23 | defer zstdEncoderPool.Put(encoder) 24 | 25 | var buf bytes.Buffer 26 | 27 | encoder.Reset(&buf) 28 | 29 | if _, err := encoder.Write(data); err != nil { 30 | return nil, err 31 | } 32 | 33 | if err := encoder.Close(); err != nil { 34 | return nil, err 35 | } 36 | 37 | return buf.Bytes(), nil 38 | } 39 | -------------------------------------------------------------------------------- /kasada.go: -------------------------------------------------------------------------------- 1 | package hyper 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "fmt" 7 | ) 8 | 9 | // GenerateKasadaPayload returns the payload to POST to /tl in bytes, and the generated headers 10 | func (s *Session) GenerateKasadaPayload(ctx context.Context, input *KasadaPayloadInput) ([]byte, *KasadaHeaders, error) { 11 | response, err := sendRequest[*KasadaPayloadInput, *kasadaPayloadOutput](ctx, s, "https://kasada.hypersolutions.co/payload", input) 12 | if err != nil { 13 | return nil, nil, err 14 | } 15 | 16 | if response.Error != "" { 17 | return nil, nil, fmt.Errorf("api returned with: %s", response.Error) 18 | } 19 | 20 | decodedPayload, err := base64.StdEncoding.DecodeString(response.Payload) 21 | if err != nil { 22 | return nil, nil, err 23 | } 24 | 25 | return decodedPayload, &response.Headers, nil 26 | } 27 | 28 | // GenerateKasadaPow returns the x-kpsdk-cd value 29 | func (s *Session) GenerateKasadaPow(ctx context.Context, input *KasadaPowInput) (string, error) { 30 | response, err := sendRequest[*KasadaPowInput, *apiResponse](ctx, s, "https://kasada.hypersolutions.co/cd", input) 31 | if err != nil { 32 | return "", err 33 | } 34 | 35 | if response.Error != "" { 36 | return "", fmt.Errorf("api returned with: %s", response.Error) 37 | } 38 | 39 | return response.Payload, nil 40 | } 41 | -------------------------------------------------------------------------------- /kasada/script_path.go: -------------------------------------------------------------------------------- 1 | package kasada 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | scriptPathExpr = regexp.MustCompile(` 0 { 280 | out.RawByte(',') 281 | } 282 | out.String(string(v3)) 283 | } 284 | out.RawByte(']') 285 | } 286 | } 287 | { 288 | const prefix string = ",\"script\":" 289 | out.RawString(prefix) 290 | out.String(string(in.Script)) 291 | } 292 | out.RawByte('}') 293 | } 294 | 295 | // MarshalJSON supports json.Marshaler interface 296 | func (v UtmvcInput) MarshalJSON() ([]byte, error) { 297 | w := jwriter.Writer{} 298 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo2(&w, v) 299 | return w.Buffer.BuildBytes(), w.Error 300 | } 301 | 302 | // MarshalEasyJSON supports easyjson.Marshaler interface 303 | func (v UtmvcInput) MarshalEasyJSON(w *jwriter.Writer) { 304 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo2(w, v) 305 | } 306 | 307 | // UnmarshalJSON supports json.Unmarshaler interface 308 | func (v *UtmvcInput) UnmarshalJSON(data []byte) error { 309 | r := jlexer.Lexer{Data: data} 310 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo2(&r, v) 311 | return r.Error() 312 | } 313 | 314 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 315 | func (v *UtmvcInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 316 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo2(l, v) 317 | } 318 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo3(in *jlexer.Lexer, out *SensorInput) { 319 | isTopLevel := in.IsStart() 320 | if in.IsNull() { 321 | if isTopLevel { 322 | in.Consumed() 323 | } 324 | in.Skip() 325 | return 326 | } 327 | in.Delim('{') 328 | for !in.IsDelim('}') { 329 | key := in.UnsafeFieldName(false) 330 | in.WantColon() 331 | if in.IsNull() { 332 | in.Skip() 333 | in.WantComma() 334 | continue 335 | } 336 | switch key { 337 | case "abck": 338 | out.Abck = string(in.String()) 339 | case "bmsz": 340 | out.Bmsz = string(in.String()) 341 | case "version": 342 | out.Version = string(in.String()) 343 | case "pageUrl": 344 | out.PageUrl = string(in.String()) 345 | case "userAgent": 346 | out.UserAgent = string(in.String()) 347 | case "scriptHash": 348 | out.ScriptHash = string(in.String()) 349 | case "dynamicValues": 350 | out.DynamicValues = string(in.String()) 351 | case "acceptLanguage": 352 | out.AcceptLanguage = string(in.String()) 353 | case "ip": 354 | out.IP = string(in.String()) 355 | case "context": 356 | out.Context = string(in.String()) 357 | default: 358 | in.SkipRecursive() 359 | } 360 | in.WantComma() 361 | } 362 | in.Delim('}') 363 | if isTopLevel { 364 | in.Consumed() 365 | } 366 | } 367 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo3(out *jwriter.Writer, in SensorInput) { 368 | out.RawByte('{') 369 | first := true 370 | _ = first 371 | { 372 | const prefix string = ",\"abck\":" 373 | out.RawString(prefix[1:]) 374 | out.String(string(in.Abck)) 375 | } 376 | { 377 | const prefix string = ",\"bmsz\":" 378 | out.RawString(prefix) 379 | out.String(string(in.Bmsz)) 380 | } 381 | { 382 | const prefix string = ",\"version\":" 383 | out.RawString(prefix) 384 | out.String(string(in.Version)) 385 | } 386 | { 387 | const prefix string = ",\"pageUrl\":" 388 | out.RawString(prefix) 389 | out.String(string(in.PageUrl)) 390 | } 391 | { 392 | const prefix string = ",\"userAgent\":" 393 | out.RawString(prefix) 394 | out.String(string(in.UserAgent)) 395 | } 396 | { 397 | const prefix string = ",\"scriptHash\":" 398 | out.RawString(prefix) 399 | out.String(string(in.ScriptHash)) 400 | } 401 | { 402 | const prefix string = ",\"dynamicValues\":" 403 | out.RawString(prefix) 404 | out.String(string(in.DynamicValues)) 405 | } 406 | { 407 | const prefix string = ",\"acceptLanguage\":" 408 | out.RawString(prefix) 409 | out.String(string(in.AcceptLanguage)) 410 | } 411 | { 412 | const prefix string = ",\"ip\":" 413 | out.RawString(prefix) 414 | out.String(string(in.IP)) 415 | } 416 | { 417 | const prefix string = ",\"context\":" 418 | out.RawString(prefix) 419 | out.String(string(in.Context)) 420 | } 421 | out.RawByte('}') 422 | } 423 | 424 | // MarshalJSON supports json.Marshaler interface 425 | func (v SensorInput) MarshalJSON() ([]byte, error) { 426 | w := jwriter.Writer{} 427 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo3(&w, v) 428 | return w.Buffer.BuildBytes(), w.Error 429 | } 430 | 431 | // MarshalEasyJSON supports easyjson.Marshaler interface 432 | func (v SensorInput) MarshalEasyJSON(w *jwriter.Writer) { 433 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo3(w, v) 434 | } 435 | 436 | // UnmarshalJSON supports json.Unmarshaler interface 437 | func (v *SensorInput) UnmarshalJSON(data []byte) error { 438 | r := jlexer.Lexer{Data: data} 439 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo3(&r, v) 440 | return r.Error() 441 | } 442 | 443 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 444 | func (v *SensorInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 445 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo3(l, v) 446 | } 447 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo4(in *jlexer.Lexer, out *SbsdInput) { 448 | isTopLevel := in.IsStart() 449 | if in.IsNull() { 450 | if isTopLevel { 451 | in.Consumed() 452 | } 453 | in.Skip() 454 | return 455 | } 456 | in.Delim('{') 457 | for !in.IsDelim('}') { 458 | key := in.UnsafeFieldName(false) 459 | in.WantColon() 460 | if in.IsNull() { 461 | in.Skip() 462 | in.WantComma() 463 | continue 464 | } 465 | switch key { 466 | case "userAgent": 467 | out.UserAgent = string(in.String()) 468 | case "uuid": 469 | out.Uuid = string(in.String()) 470 | case "pageUrl": 471 | out.PageUrl = string(in.String()) 472 | case "o": 473 | out.OCookie = string(in.String()) 474 | case "script": 475 | out.Script = string(in.String()) 476 | case "acceptLanguage": 477 | out.AcceptLanguage = string(in.String()) 478 | case "ip": 479 | out.IP = string(in.String()) 480 | default: 481 | in.SkipRecursive() 482 | } 483 | in.WantComma() 484 | } 485 | in.Delim('}') 486 | if isTopLevel { 487 | in.Consumed() 488 | } 489 | } 490 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo4(out *jwriter.Writer, in SbsdInput) { 491 | out.RawByte('{') 492 | first := true 493 | _ = first 494 | { 495 | const prefix string = ",\"userAgent\":" 496 | out.RawString(prefix[1:]) 497 | out.String(string(in.UserAgent)) 498 | } 499 | { 500 | const prefix string = ",\"uuid\":" 501 | out.RawString(prefix) 502 | out.String(string(in.Uuid)) 503 | } 504 | { 505 | const prefix string = ",\"pageUrl\":" 506 | out.RawString(prefix) 507 | out.String(string(in.PageUrl)) 508 | } 509 | { 510 | const prefix string = ",\"o\":" 511 | out.RawString(prefix) 512 | out.String(string(in.OCookie)) 513 | } 514 | { 515 | const prefix string = ",\"script\":" 516 | out.RawString(prefix) 517 | out.String(string(in.Script)) 518 | } 519 | { 520 | const prefix string = ",\"acceptLanguage\":" 521 | out.RawString(prefix) 522 | out.String(string(in.AcceptLanguage)) 523 | } 524 | { 525 | const prefix string = ",\"ip\":" 526 | out.RawString(prefix) 527 | out.String(string(in.IP)) 528 | } 529 | out.RawByte('}') 530 | } 531 | 532 | // MarshalJSON supports json.Marshaler interface 533 | func (v SbsdInput) MarshalJSON() ([]byte, error) { 534 | w := jwriter.Writer{} 535 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo4(&w, v) 536 | return w.Buffer.BuildBytes(), w.Error 537 | } 538 | 539 | // MarshalEasyJSON supports easyjson.Marshaler interface 540 | func (v SbsdInput) MarshalEasyJSON(w *jwriter.Writer) { 541 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo4(w, v) 542 | } 543 | 544 | // UnmarshalJSON supports json.Unmarshaler interface 545 | func (v *SbsdInput) UnmarshalJSON(data []byte) error { 546 | r := jlexer.Lexer{Data: data} 547 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo4(&r, v) 548 | return r.Error() 549 | } 550 | 551 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 552 | func (v *SbsdInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 553 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo4(l, v) 554 | } 555 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo5(in *jlexer.Lexer, out *ReeseInput) { 556 | isTopLevel := in.IsStart() 557 | if in.IsNull() { 558 | if isTopLevel { 559 | in.Consumed() 560 | } 561 | in.Skip() 562 | return 563 | } 564 | in.Delim('{') 565 | for !in.IsDelim('}') { 566 | key := in.UnsafeFieldName(false) 567 | in.WantColon() 568 | if in.IsNull() { 569 | in.Skip() 570 | in.WantComma() 571 | continue 572 | } 573 | switch key { 574 | case "userAgent": 575 | out.UserAgent = string(in.String()) 576 | case "acceptLanguage": 577 | out.AcceptLanguage = string(in.String()) 578 | case "ip": 579 | out.IP = string(in.String()) 580 | case "scriptUrl": 581 | out.ScriptUrl = string(in.String()) 582 | case "pageUrl": 583 | out.PageUrl = string(in.String()) 584 | default: 585 | in.SkipRecursive() 586 | } 587 | in.WantComma() 588 | } 589 | in.Delim('}') 590 | if isTopLevel { 591 | in.Consumed() 592 | } 593 | } 594 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo5(out *jwriter.Writer, in ReeseInput) { 595 | out.RawByte('{') 596 | first := true 597 | _ = first 598 | { 599 | const prefix string = ",\"userAgent\":" 600 | out.RawString(prefix[1:]) 601 | out.String(string(in.UserAgent)) 602 | } 603 | { 604 | const prefix string = ",\"acceptLanguage\":" 605 | out.RawString(prefix) 606 | out.String(string(in.AcceptLanguage)) 607 | } 608 | { 609 | const prefix string = ",\"ip\":" 610 | out.RawString(prefix) 611 | out.String(string(in.IP)) 612 | } 613 | { 614 | const prefix string = ",\"scriptUrl\":" 615 | out.RawString(prefix) 616 | out.String(string(in.ScriptUrl)) 617 | } 618 | { 619 | const prefix string = ",\"pageUrl\":" 620 | out.RawString(prefix) 621 | out.String(string(in.PageUrl)) 622 | } 623 | out.RawByte('}') 624 | } 625 | 626 | // MarshalJSON supports json.Marshaler interface 627 | func (v ReeseInput) MarshalJSON() ([]byte, error) { 628 | w := jwriter.Writer{} 629 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo5(&w, v) 630 | return w.Buffer.BuildBytes(), w.Error 631 | } 632 | 633 | // MarshalEasyJSON supports easyjson.Marshaler interface 634 | func (v ReeseInput) MarshalEasyJSON(w *jwriter.Writer) { 635 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo5(w, v) 636 | } 637 | 638 | // UnmarshalJSON supports json.Unmarshaler interface 639 | func (v *ReeseInput) UnmarshalJSON(data []byte) error { 640 | r := jlexer.Lexer{Data: data} 641 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo5(&r, v) 642 | return r.Error() 643 | } 644 | 645 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 646 | func (v *ReeseInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 647 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo5(l, v) 648 | } 649 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo6(in *jlexer.Lexer, out *PixelInput) { 650 | isTopLevel := in.IsStart() 651 | if in.IsNull() { 652 | if isTopLevel { 653 | in.Consumed() 654 | } 655 | in.Skip() 656 | return 657 | } 658 | in.Delim('{') 659 | for !in.IsDelim('}') { 660 | key := in.UnsafeFieldName(false) 661 | in.WantColon() 662 | if in.IsNull() { 663 | in.Skip() 664 | in.WantComma() 665 | continue 666 | } 667 | switch key { 668 | case "userAgent": 669 | out.UserAgent = string(in.String()) 670 | case "htmlVar": 671 | out.HTMLVar = string(in.String()) 672 | case "scriptVar": 673 | out.ScriptVar = string(in.String()) 674 | case "acceptLanguage": 675 | out.AcceptLanguage = string(in.String()) 676 | case "ip": 677 | out.IP = string(in.String()) 678 | default: 679 | in.SkipRecursive() 680 | } 681 | in.WantComma() 682 | } 683 | in.Delim('}') 684 | if isTopLevel { 685 | in.Consumed() 686 | } 687 | } 688 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo6(out *jwriter.Writer, in PixelInput) { 689 | out.RawByte('{') 690 | first := true 691 | _ = first 692 | { 693 | const prefix string = ",\"userAgent\":" 694 | out.RawString(prefix[1:]) 695 | out.String(string(in.UserAgent)) 696 | } 697 | { 698 | const prefix string = ",\"htmlVar\":" 699 | out.RawString(prefix) 700 | out.String(string(in.HTMLVar)) 701 | } 702 | { 703 | const prefix string = ",\"scriptVar\":" 704 | out.RawString(prefix) 705 | out.String(string(in.ScriptVar)) 706 | } 707 | { 708 | const prefix string = ",\"acceptLanguage\":" 709 | out.RawString(prefix) 710 | out.String(string(in.AcceptLanguage)) 711 | } 712 | { 713 | const prefix string = ",\"ip\":" 714 | out.RawString(prefix) 715 | out.String(string(in.IP)) 716 | } 717 | out.RawByte('}') 718 | } 719 | 720 | // MarshalJSON supports json.Marshaler interface 721 | func (v PixelInput) MarshalJSON() ([]byte, error) { 722 | w := jwriter.Writer{} 723 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo6(&w, v) 724 | return w.Buffer.BuildBytes(), w.Error 725 | } 726 | 727 | // MarshalEasyJSON supports easyjson.Marshaler interface 728 | func (v PixelInput) MarshalEasyJSON(w *jwriter.Writer) { 729 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo6(w, v) 730 | } 731 | 732 | // UnmarshalJSON supports json.Unmarshaler interface 733 | func (v *PixelInput) UnmarshalJSON(data []byte) error { 734 | r := jlexer.Lexer{Data: data} 735 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo6(&r, v) 736 | return r.Error() 737 | } 738 | 739 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 740 | func (v *PixelInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 741 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo6(l, v) 742 | } 743 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo7(in *jlexer.Lexer, out *KasadaPowInput) { 744 | isTopLevel := in.IsStart() 745 | if in.IsNull() { 746 | if isTopLevel { 747 | in.Consumed() 748 | } 749 | in.Skip() 750 | return 751 | } 752 | in.Delim('{') 753 | for !in.IsDelim('}') { 754 | key := in.UnsafeFieldName(false) 755 | in.WantColon() 756 | if in.IsNull() { 757 | in.Skip() 758 | in.WantComma() 759 | continue 760 | } 761 | switch key { 762 | case "st": 763 | out.St = int(in.Int()) 764 | case "ct": 765 | out.Ct = string(in.String()) 766 | case "workTime": 767 | if in.IsNull() { 768 | in.Skip() 769 | out.WorkTime = nil 770 | } else { 771 | if out.WorkTime == nil { 772 | out.WorkTime = new(int) 773 | } 774 | *out.WorkTime = int(in.Int()) 775 | } 776 | default: 777 | in.SkipRecursive() 778 | } 779 | in.WantComma() 780 | } 781 | in.Delim('}') 782 | if isTopLevel { 783 | in.Consumed() 784 | } 785 | } 786 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo7(out *jwriter.Writer, in KasadaPowInput) { 787 | out.RawByte('{') 788 | first := true 789 | _ = first 790 | { 791 | const prefix string = ",\"st\":" 792 | out.RawString(prefix[1:]) 793 | out.Int(int(in.St)) 794 | } 795 | { 796 | const prefix string = ",\"ct\":" 797 | out.RawString(prefix) 798 | out.String(string(in.Ct)) 799 | } 800 | if in.WorkTime != nil { 801 | const prefix string = ",\"workTime\":" 802 | out.RawString(prefix) 803 | out.Int(int(*in.WorkTime)) 804 | } 805 | out.RawByte('}') 806 | } 807 | 808 | // MarshalJSON supports json.Marshaler interface 809 | func (v KasadaPowInput) MarshalJSON() ([]byte, error) { 810 | w := jwriter.Writer{} 811 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo7(&w, v) 812 | return w.Buffer.BuildBytes(), w.Error 813 | } 814 | 815 | // MarshalEasyJSON supports easyjson.Marshaler interface 816 | func (v KasadaPowInput) MarshalEasyJSON(w *jwriter.Writer) { 817 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo7(w, v) 818 | } 819 | 820 | // UnmarshalJSON supports json.Unmarshaler interface 821 | func (v *KasadaPowInput) UnmarshalJSON(data []byte) error { 822 | r := jlexer.Lexer{Data: data} 823 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo7(&r, v) 824 | return r.Error() 825 | } 826 | 827 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 828 | func (v *KasadaPowInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 829 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo7(l, v) 830 | } 831 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo8(in *jlexer.Lexer, out *KasadaPayloadInput) { 832 | isTopLevel := in.IsStart() 833 | if in.IsNull() { 834 | if isTopLevel { 835 | in.Consumed() 836 | } 837 | in.Skip() 838 | return 839 | } 840 | in.Delim('{') 841 | for !in.IsDelim('}') { 842 | key := in.UnsafeFieldName(false) 843 | in.WantColon() 844 | if in.IsNull() { 845 | in.Skip() 846 | in.WantComma() 847 | continue 848 | } 849 | switch key { 850 | case "userAgent": 851 | out.UserAgent = string(in.String()) 852 | case "ipsLink": 853 | out.IpsLink = string(in.String()) 854 | case "script": 855 | out.Script = string(in.String()) 856 | case "acceptLanguage": 857 | out.AcceptLanguage = string(in.String()) 858 | case "ip": 859 | out.IP = string(in.String()) 860 | default: 861 | in.SkipRecursive() 862 | } 863 | in.WantComma() 864 | } 865 | in.Delim('}') 866 | if isTopLevel { 867 | in.Consumed() 868 | } 869 | } 870 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo8(out *jwriter.Writer, in KasadaPayloadInput) { 871 | out.RawByte('{') 872 | first := true 873 | _ = first 874 | { 875 | const prefix string = ",\"userAgent\":" 876 | out.RawString(prefix[1:]) 877 | out.String(string(in.UserAgent)) 878 | } 879 | { 880 | const prefix string = ",\"ipsLink\":" 881 | out.RawString(prefix) 882 | out.String(string(in.IpsLink)) 883 | } 884 | { 885 | const prefix string = ",\"script\":" 886 | out.RawString(prefix) 887 | out.String(string(in.Script)) 888 | } 889 | { 890 | const prefix string = ",\"acceptLanguage\":" 891 | out.RawString(prefix) 892 | out.String(string(in.AcceptLanguage)) 893 | } 894 | if in.IP != "" { 895 | const prefix string = ",\"ip\":" 896 | out.RawString(prefix) 897 | out.String(string(in.IP)) 898 | } 899 | out.RawByte('}') 900 | } 901 | 902 | // MarshalJSON supports json.Marshaler interface 903 | func (v KasadaPayloadInput) MarshalJSON() ([]byte, error) { 904 | w := jwriter.Writer{} 905 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo8(&w, v) 906 | return w.Buffer.BuildBytes(), w.Error 907 | } 908 | 909 | // MarshalEasyJSON supports easyjson.Marshaler interface 910 | func (v KasadaPayloadInput) MarshalEasyJSON(w *jwriter.Writer) { 911 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo8(w, v) 912 | } 913 | 914 | // UnmarshalJSON supports json.Unmarshaler interface 915 | func (v *KasadaPayloadInput) UnmarshalJSON(data []byte) error { 916 | r := jlexer.Lexer{Data: data} 917 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo8(&r, v) 918 | return r.Error() 919 | } 920 | 921 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 922 | func (v *KasadaPayloadInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 923 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo8(l, v) 924 | } 925 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo9(in *jlexer.Lexer, out *KasadaHeaders) { 926 | isTopLevel := in.IsStart() 927 | if in.IsNull() { 928 | if isTopLevel { 929 | in.Consumed() 930 | } 931 | in.Skip() 932 | return 933 | } 934 | in.Delim('{') 935 | for !in.IsDelim('}') { 936 | key := in.UnsafeFieldName(false) 937 | in.WantColon() 938 | if in.IsNull() { 939 | in.Skip() 940 | in.WantComma() 941 | continue 942 | } 943 | switch key { 944 | case "x-kpsdk-ct": 945 | out.XKpsdkCt = string(in.String()) 946 | case "x-kpsdk-dt": 947 | out.XKpsdkDt = string(in.String()) 948 | case "x-kpsdk-v": 949 | out.XKpsdkV = string(in.String()) 950 | case "x-kpsdk-r": 951 | out.XKpsdkR = string(in.String()) 952 | case "x-kpsdk-dv": 953 | out.XKpsdkDv = string(in.String()) 954 | case "x-kpsdk-h": 955 | out.XKpsdkH = string(in.String()) 956 | case "x-kpsdk-fc": 957 | out.XKpsdkFc = string(in.String()) 958 | case "x-kpsdk-im": 959 | out.XKpsdkIm = string(in.String()) 960 | default: 961 | in.SkipRecursive() 962 | } 963 | in.WantComma() 964 | } 965 | in.Delim('}') 966 | if isTopLevel { 967 | in.Consumed() 968 | } 969 | } 970 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo9(out *jwriter.Writer, in KasadaHeaders) { 971 | out.RawByte('{') 972 | first := true 973 | _ = first 974 | { 975 | const prefix string = ",\"x-kpsdk-ct\":" 976 | out.RawString(prefix[1:]) 977 | out.String(string(in.XKpsdkCt)) 978 | } 979 | { 980 | const prefix string = ",\"x-kpsdk-dt\":" 981 | out.RawString(prefix) 982 | out.String(string(in.XKpsdkDt)) 983 | } 984 | { 985 | const prefix string = ",\"x-kpsdk-v\":" 986 | out.RawString(prefix) 987 | out.String(string(in.XKpsdkV)) 988 | } 989 | { 990 | const prefix string = ",\"x-kpsdk-r\":" 991 | out.RawString(prefix) 992 | out.String(string(in.XKpsdkR)) 993 | } 994 | { 995 | const prefix string = ",\"x-kpsdk-dv\":" 996 | out.RawString(prefix) 997 | out.String(string(in.XKpsdkDv)) 998 | } 999 | { 1000 | const prefix string = ",\"x-kpsdk-h\":" 1001 | out.RawString(prefix) 1002 | out.String(string(in.XKpsdkH)) 1003 | } 1004 | { 1005 | const prefix string = ",\"x-kpsdk-fc\":" 1006 | out.RawString(prefix) 1007 | out.String(string(in.XKpsdkFc)) 1008 | } 1009 | { 1010 | const prefix string = ",\"x-kpsdk-im\":" 1011 | out.RawString(prefix) 1012 | out.String(string(in.XKpsdkIm)) 1013 | } 1014 | out.RawByte('}') 1015 | } 1016 | 1017 | // MarshalJSON supports json.Marshaler interface 1018 | func (v KasadaHeaders) MarshalJSON() ([]byte, error) { 1019 | w := jwriter.Writer{} 1020 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo9(&w, v) 1021 | return w.Buffer.BuildBytes(), w.Error 1022 | } 1023 | 1024 | // MarshalEasyJSON supports easyjson.Marshaler interface 1025 | func (v KasadaHeaders) MarshalEasyJSON(w *jwriter.Writer) { 1026 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo9(w, v) 1027 | } 1028 | 1029 | // UnmarshalJSON supports json.Unmarshaler interface 1030 | func (v *KasadaHeaders) UnmarshalJSON(data []byte) error { 1031 | r := jlexer.Lexer{Data: data} 1032 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo9(&r, v) 1033 | return r.Error() 1034 | } 1035 | 1036 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1037 | func (v *KasadaHeaders) UnmarshalEasyJSON(l *jlexer.Lexer) { 1038 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo9(l, v) 1039 | } 1040 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo10(in *jlexer.Lexer, out *Headers) { 1041 | isTopLevel := in.IsStart() 1042 | if in.IsNull() { 1043 | if isTopLevel { 1044 | in.Consumed() 1045 | } 1046 | in.Skip() 1047 | return 1048 | } 1049 | in.Delim('{') 1050 | for !in.IsDelim('}') { 1051 | key := in.UnsafeFieldName(false) 1052 | in.WantColon() 1053 | if in.IsNull() { 1054 | in.Skip() 1055 | in.WantComma() 1056 | continue 1057 | } 1058 | switch key { 1059 | case "sec-ch-device-memory": 1060 | out.DeviceMemory = string(in.String()) 1061 | case "sec-ch-ua-mobile": 1062 | out.Mobile = string(in.String()) 1063 | case "sec-ch-ua-arch": 1064 | out.Arch = string(in.String()) 1065 | case "sec-ch-ua-platform": 1066 | out.Platform = string(in.String()) 1067 | case "sec-ch-ua-model": 1068 | out.Model = string(in.String()) 1069 | case "sec-ch-ua-full-version-list": 1070 | out.FullVersionList = string(in.String()) 1071 | default: 1072 | in.SkipRecursive() 1073 | } 1074 | in.WantComma() 1075 | } 1076 | in.Delim('}') 1077 | if isTopLevel { 1078 | in.Consumed() 1079 | } 1080 | } 1081 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo10(out *jwriter.Writer, in Headers) { 1082 | out.RawByte('{') 1083 | first := true 1084 | _ = first 1085 | { 1086 | const prefix string = ",\"sec-ch-device-memory\":" 1087 | out.RawString(prefix[1:]) 1088 | out.String(string(in.DeviceMemory)) 1089 | } 1090 | { 1091 | const prefix string = ",\"sec-ch-ua-mobile\":" 1092 | out.RawString(prefix) 1093 | out.String(string(in.Mobile)) 1094 | } 1095 | { 1096 | const prefix string = ",\"sec-ch-ua-arch\":" 1097 | out.RawString(prefix) 1098 | out.String(string(in.Arch)) 1099 | } 1100 | { 1101 | const prefix string = ",\"sec-ch-ua-platform\":" 1102 | out.RawString(prefix) 1103 | out.String(string(in.Platform)) 1104 | } 1105 | { 1106 | const prefix string = ",\"sec-ch-ua-model\":" 1107 | out.RawString(prefix) 1108 | out.String(string(in.Model)) 1109 | } 1110 | { 1111 | const prefix string = ",\"sec-ch-ua-full-version-list\":" 1112 | out.RawString(prefix) 1113 | out.String(string(in.FullVersionList)) 1114 | } 1115 | out.RawByte('}') 1116 | } 1117 | 1118 | // MarshalJSON supports json.Marshaler interface 1119 | func (v Headers) MarshalJSON() ([]byte, error) { 1120 | w := jwriter.Writer{} 1121 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo10(&w, v) 1122 | return w.Buffer.BuildBytes(), w.Error 1123 | } 1124 | 1125 | // MarshalEasyJSON supports easyjson.Marshaler interface 1126 | func (v Headers) MarshalEasyJSON(w *jwriter.Writer) { 1127 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo10(w, v) 1128 | } 1129 | 1130 | // UnmarshalJSON supports json.Unmarshaler interface 1131 | func (v *Headers) UnmarshalJSON(data []byte) error { 1132 | r := jlexer.Lexer{Data: data} 1133 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo10(&r, v) 1134 | return r.Error() 1135 | } 1136 | 1137 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1138 | func (v *Headers) UnmarshalEasyJSON(l *jlexer.Lexer) { 1139 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo10(l, v) 1140 | } 1141 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo11(in *jlexer.Lexer, out *DynamicInput) { 1142 | isTopLevel := in.IsStart() 1143 | if in.IsNull() { 1144 | if isTopLevel { 1145 | in.Consumed() 1146 | } 1147 | in.Skip() 1148 | return 1149 | } 1150 | in.Delim('{') 1151 | for !in.IsDelim('}') { 1152 | key := in.UnsafeFieldName(false) 1153 | in.WantColon() 1154 | if in.IsNull() { 1155 | in.Skip() 1156 | in.WantComma() 1157 | continue 1158 | } 1159 | switch key { 1160 | case "script": 1161 | out.Script = string(in.String()) 1162 | default: 1163 | in.SkipRecursive() 1164 | } 1165 | in.WantComma() 1166 | } 1167 | in.Delim('}') 1168 | if isTopLevel { 1169 | in.Consumed() 1170 | } 1171 | } 1172 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo11(out *jwriter.Writer, in DynamicInput) { 1173 | out.RawByte('{') 1174 | first := true 1175 | _ = first 1176 | { 1177 | const prefix string = ",\"script\":" 1178 | out.RawString(prefix[1:]) 1179 | out.String(string(in.Script)) 1180 | } 1181 | out.RawByte('}') 1182 | } 1183 | 1184 | // MarshalJSON supports json.Marshaler interface 1185 | func (v DynamicInput) MarshalJSON() ([]byte, error) { 1186 | w := jwriter.Writer{} 1187 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo11(&w, v) 1188 | return w.Buffer.BuildBytes(), w.Error 1189 | } 1190 | 1191 | // MarshalEasyJSON supports easyjson.Marshaler interface 1192 | func (v DynamicInput) MarshalEasyJSON(w *jwriter.Writer) { 1193 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo11(w, v) 1194 | } 1195 | 1196 | // UnmarshalJSON supports json.Unmarshaler interface 1197 | func (v *DynamicInput) UnmarshalJSON(data []byte) error { 1198 | r := jlexer.Lexer{Data: data} 1199 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo11(&r, v) 1200 | return r.Error() 1201 | } 1202 | 1203 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1204 | func (v *DynamicInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 1205 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo11(l, v) 1206 | } 1207 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo12(in *jlexer.Lexer, out *DataDomeTagsInput) { 1208 | isTopLevel := in.IsStart() 1209 | if in.IsNull() { 1210 | if isTopLevel { 1211 | in.Consumed() 1212 | } 1213 | in.Skip() 1214 | return 1215 | } 1216 | in.Delim('{') 1217 | for !in.IsDelim('}') { 1218 | key := in.UnsafeFieldName(false) 1219 | in.WantColon() 1220 | if in.IsNull() { 1221 | in.Skip() 1222 | in.WantComma() 1223 | continue 1224 | } 1225 | switch key { 1226 | case "userAgent": 1227 | out.UserAgent = string(in.String()) 1228 | case "cid": 1229 | out.Cid = string(in.String()) 1230 | case "ddk": 1231 | out.Ddk = string(in.String()) 1232 | case "referer": 1233 | out.Referer = string(in.String()) 1234 | case "type": 1235 | out.Type = string(in.String()) 1236 | case "version": 1237 | out.Version = string(in.String()) 1238 | case "acceptLanguage": 1239 | out.AcceptLanguage = string(in.String()) 1240 | case "ip": 1241 | out.IP = string(in.String()) 1242 | default: 1243 | in.SkipRecursive() 1244 | } 1245 | in.WantComma() 1246 | } 1247 | in.Delim('}') 1248 | if isTopLevel { 1249 | in.Consumed() 1250 | } 1251 | } 1252 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo12(out *jwriter.Writer, in DataDomeTagsInput) { 1253 | out.RawByte('{') 1254 | first := true 1255 | _ = first 1256 | { 1257 | const prefix string = ",\"userAgent\":" 1258 | out.RawString(prefix[1:]) 1259 | out.String(string(in.UserAgent)) 1260 | } 1261 | { 1262 | const prefix string = ",\"cid\":" 1263 | out.RawString(prefix) 1264 | out.String(string(in.Cid)) 1265 | } 1266 | { 1267 | const prefix string = ",\"ddk\":" 1268 | out.RawString(prefix) 1269 | out.String(string(in.Ddk)) 1270 | } 1271 | { 1272 | const prefix string = ",\"referer\":" 1273 | out.RawString(prefix) 1274 | out.String(string(in.Referer)) 1275 | } 1276 | { 1277 | const prefix string = ",\"type\":" 1278 | out.RawString(prefix) 1279 | out.String(string(in.Type)) 1280 | } 1281 | { 1282 | const prefix string = ",\"version\":" 1283 | out.RawString(prefix) 1284 | out.String(string(in.Version)) 1285 | } 1286 | { 1287 | const prefix string = ",\"acceptLanguage\":" 1288 | out.RawString(prefix) 1289 | out.String(string(in.AcceptLanguage)) 1290 | } 1291 | { 1292 | const prefix string = ",\"ip\":" 1293 | out.RawString(prefix) 1294 | out.String(string(in.IP)) 1295 | } 1296 | out.RawByte('}') 1297 | } 1298 | 1299 | // MarshalJSON supports json.Marshaler interface 1300 | func (v DataDomeTagsInput) MarshalJSON() ([]byte, error) { 1301 | w := jwriter.Writer{} 1302 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo12(&w, v) 1303 | return w.Buffer.BuildBytes(), w.Error 1304 | } 1305 | 1306 | // MarshalEasyJSON supports easyjson.Marshaler interface 1307 | func (v DataDomeTagsInput) MarshalEasyJSON(w *jwriter.Writer) { 1308 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo12(w, v) 1309 | } 1310 | 1311 | // UnmarshalJSON supports json.Unmarshaler interface 1312 | func (v *DataDomeTagsInput) UnmarshalJSON(data []byte) error { 1313 | r := jlexer.Lexer{Data: data} 1314 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo12(&r, v) 1315 | return r.Error() 1316 | } 1317 | 1318 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1319 | func (v *DataDomeTagsInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 1320 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo12(l, v) 1321 | } 1322 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo13(in *jlexer.Lexer, out *DataDomeSliderInput) { 1323 | isTopLevel := in.IsStart() 1324 | if in.IsNull() { 1325 | if isTopLevel { 1326 | in.Consumed() 1327 | } 1328 | in.Skip() 1329 | return 1330 | } 1331 | in.Delim('{') 1332 | for !in.IsDelim('}') { 1333 | key := in.UnsafeFieldName(false) 1334 | in.WantColon() 1335 | if in.IsNull() { 1336 | in.Skip() 1337 | in.WantComma() 1338 | continue 1339 | } 1340 | switch key { 1341 | case "userAgent": 1342 | out.UserAgent = string(in.String()) 1343 | case "deviceLink": 1344 | out.DeviceLink = string(in.String()) 1345 | case "html": 1346 | out.Html = string(in.String()) 1347 | case "puzzle": 1348 | out.Puzzle = string(in.String()) 1349 | case "piece": 1350 | out.Piece = string(in.String()) 1351 | case "parentUrl": 1352 | out.ParentUrl = string(in.String()) 1353 | case "acceptLanguage": 1354 | out.AcceptLanguage = string(in.String()) 1355 | case "ip": 1356 | out.IP = string(in.String()) 1357 | default: 1358 | in.SkipRecursive() 1359 | } 1360 | in.WantComma() 1361 | } 1362 | in.Delim('}') 1363 | if isTopLevel { 1364 | in.Consumed() 1365 | } 1366 | } 1367 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo13(out *jwriter.Writer, in DataDomeSliderInput) { 1368 | out.RawByte('{') 1369 | first := true 1370 | _ = first 1371 | { 1372 | const prefix string = ",\"userAgent\":" 1373 | out.RawString(prefix[1:]) 1374 | out.String(string(in.UserAgent)) 1375 | } 1376 | { 1377 | const prefix string = ",\"deviceLink\":" 1378 | out.RawString(prefix) 1379 | out.String(string(in.DeviceLink)) 1380 | } 1381 | { 1382 | const prefix string = ",\"html\":" 1383 | out.RawString(prefix) 1384 | out.String(string(in.Html)) 1385 | } 1386 | { 1387 | const prefix string = ",\"puzzle\":" 1388 | out.RawString(prefix) 1389 | out.String(string(in.Puzzle)) 1390 | } 1391 | { 1392 | const prefix string = ",\"piece\":" 1393 | out.RawString(prefix) 1394 | out.String(string(in.Piece)) 1395 | } 1396 | { 1397 | const prefix string = ",\"parentUrl\":" 1398 | out.RawString(prefix) 1399 | out.String(string(in.ParentUrl)) 1400 | } 1401 | { 1402 | const prefix string = ",\"acceptLanguage\":" 1403 | out.RawString(prefix) 1404 | out.String(string(in.AcceptLanguage)) 1405 | } 1406 | { 1407 | const prefix string = ",\"ip\":" 1408 | out.RawString(prefix) 1409 | out.String(string(in.IP)) 1410 | } 1411 | out.RawByte('}') 1412 | } 1413 | 1414 | // MarshalJSON supports json.Marshaler interface 1415 | func (v DataDomeSliderInput) MarshalJSON() ([]byte, error) { 1416 | w := jwriter.Writer{} 1417 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo13(&w, v) 1418 | return w.Buffer.BuildBytes(), w.Error 1419 | } 1420 | 1421 | // MarshalEasyJSON supports easyjson.Marshaler interface 1422 | func (v DataDomeSliderInput) MarshalEasyJSON(w *jwriter.Writer) { 1423 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo13(w, v) 1424 | } 1425 | 1426 | // UnmarshalJSON supports json.Unmarshaler interface 1427 | func (v *DataDomeSliderInput) UnmarshalJSON(data []byte) error { 1428 | r := jlexer.Lexer{Data: data} 1429 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo13(&r, v) 1430 | return r.Error() 1431 | } 1432 | 1433 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1434 | func (v *DataDomeSliderInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 1435 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo13(l, v) 1436 | } 1437 | func easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo14(in *jlexer.Lexer, out *DataDomeInterstitialInput) { 1438 | isTopLevel := in.IsStart() 1439 | if in.IsNull() { 1440 | if isTopLevel { 1441 | in.Consumed() 1442 | } 1443 | in.Skip() 1444 | return 1445 | } 1446 | in.Delim('{') 1447 | for !in.IsDelim('}') { 1448 | key := in.UnsafeFieldName(false) 1449 | in.WantColon() 1450 | if in.IsNull() { 1451 | in.Skip() 1452 | in.WantComma() 1453 | continue 1454 | } 1455 | switch key { 1456 | case "userAgent": 1457 | out.UserAgent = string(in.String()) 1458 | case "deviceLink": 1459 | out.DeviceLink = string(in.String()) 1460 | case "html": 1461 | out.Html = string(in.String()) 1462 | case "acceptLanguage": 1463 | out.AcceptLanguage = string(in.String()) 1464 | case "ip": 1465 | out.IP = string(in.String()) 1466 | default: 1467 | in.SkipRecursive() 1468 | } 1469 | in.WantComma() 1470 | } 1471 | in.Delim('}') 1472 | if isTopLevel { 1473 | in.Consumed() 1474 | } 1475 | } 1476 | func easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo14(out *jwriter.Writer, in DataDomeInterstitialInput) { 1477 | out.RawByte('{') 1478 | first := true 1479 | _ = first 1480 | { 1481 | const prefix string = ",\"userAgent\":" 1482 | out.RawString(prefix[1:]) 1483 | out.String(string(in.UserAgent)) 1484 | } 1485 | { 1486 | const prefix string = ",\"deviceLink\":" 1487 | out.RawString(prefix) 1488 | out.String(string(in.DeviceLink)) 1489 | } 1490 | { 1491 | const prefix string = ",\"html\":" 1492 | out.RawString(prefix) 1493 | out.String(string(in.Html)) 1494 | } 1495 | { 1496 | const prefix string = ",\"acceptLanguage\":" 1497 | out.RawString(prefix) 1498 | out.String(string(in.AcceptLanguage)) 1499 | } 1500 | { 1501 | const prefix string = ",\"ip\":" 1502 | out.RawString(prefix) 1503 | out.String(string(in.IP)) 1504 | } 1505 | out.RawByte('}') 1506 | } 1507 | 1508 | // MarshalJSON supports json.Marshaler interface 1509 | func (v DataDomeInterstitialInput) MarshalJSON() ([]byte, error) { 1510 | w := jwriter.Writer{} 1511 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo14(&w, v) 1512 | return w.Buffer.BuildBytes(), w.Error 1513 | } 1514 | 1515 | // MarshalEasyJSON supports easyjson.Marshaler interface 1516 | func (v DataDomeInterstitialInput) MarshalEasyJSON(w *jwriter.Writer) { 1517 | easyjsonD2b7633eEncodeGithubComHyperSolutionsHyperSdkGo14(w, v) 1518 | } 1519 | 1520 | // UnmarshalJSON supports json.Unmarshaler interface 1521 | func (v *DataDomeInterstitialInput) UnmarshalJSON(data []byte) error { 1522 | r := jlexer.Lexer{Data: data} 1523 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo14(&r, v) 1524 | return r.Error() 1525 | } 1526 | 1527 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1528 | func (v *DataDomeInterstitialInput) UnmarshalEasyJSON(l *jlexer.Lexer) { 1529 | easyjsonD2b7633eDecodeGithubComHyperSolutionsHyperSdkGo14(l, v) 1530 | } 1531 | -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | package hyper 2 | 3 | import ( 4 | "github.com/golang-jwt/jwt/v5" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | type Session struct { 10 | ApiKey string 11 | JwtKey []byte 12 | Client *http.Client 13 | } 14 | 15 | // Default optimized HTTP client for concurrent requests 16 | var defaultClient = &http.Client{ 17 | Timeout: 30 * time.Second, 18 | Transport: &http.Transport{ 19 | MaxIdleConns: 0, 20 | MaxIdleConnsPerHost: 100, 21 | MaxConnsPerHost: 100, 22 | IdleConnTimeout: 30 * time.Second, 23 | }, 24 | } 25 | 26 | // NewSession creates a new Session that can be used to make requests to the Hyper Solutions API. 27 | func NewSession(apiKey string) *Session { 28 | return &Session{ 29 | ApiKey: apiKey, 30 | Client: defaultClient, 31 | } 32 | } 33 | 34 | // WithJwtKey adds the JWT Key to the session. If not empty, a signature will be added to each request. 35 | func (s *Session) WithJwtKey(jwt string) *Session { 36 | s.JwtKey = []byte(jwt) 37 | return s 38 | } 39 | 40 | // WithClient sets a new client that will be used to make requests to the Hyper Solutions API. 41 | func (s *Session) WithClient(client *http.Client) *Session { 42 | s.Client = client 43 | return s 44 | } 45 | 46 | func (s *Session) generateSignature() (string, error) { 47 | claims := jwt.MapClaims{ 48 | "key": s.ApiKey, 49 | "exp": time.Now().Add(time.Minute).Unix(), 50 | } 51 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 52 | 53 | signedToken, err := token.SignedString(s.JwtKey) 54 | if err != nil { 55 | return "", err 56 | } 57 | return signedToken, nil 58 | } 59 | --------------------------------------------------------------------------------