@@ -2,6 +2,7 @@ import { EventEmitter } from 'events'
22import { Agent as HttpAgent } from 'http'
33import { Agent as HttpsAgent } from 'https'
44import { createPromise } from './util.mjs'
5+ import { generateHashcashToken } from './crypto/index.mjs'
56
67const MAX_RETRIES = 4
78const ERRORS = {
@@ -89,7 +90,16 @@ class API extends EventEmitter {
8990 if ( this . closed && ! isLogout ) throw Error ( 'API is closed' )
9091 const [ cb , promise ] = createPromise ( originalCb )
9192
92- const qs = { id : ( this . counterId ++ ) . toString ( ) }
93+ // Don't increment counterId when re-requesting with a hashcash
94+ // Thanks @angelvega 93
95+ if ( typeof json . _hashcash !== 'string' ) {
96+ this . counterId ++
97+ }
98+
99+ const qs = {
100+ id : this . counterId . toString ( )
101+ }
102+
93103 if ( this . sid ) {
94104 qs . sid = this . sid
95105 }
@@ -99,11 +109,25 @@ class API extends EventEmitter {
99109 delete json . _querystring
100110 }
101111
112+ const headers = { 'Content-Type' : 'application/json' }
113+ if ( typeof json . _hashcash === 'string' ) {
114+ headers [ 'X-Hashcash' ] = json . _hashcash
115+ delete json . _hashcash
116+ }
117+
102118 this . fetch ( `${ this . gateway } cs?${ new URLSearchParams ( qs ) } ` , {
103119 method : 'POST' ,
104- headers : { 'Content-Type' : 'application/json' } ,
120+ headers,
105121 body : JSON . stringify ( [ json ] )
106- } ) . then ( handleApiResponse ) . then ( resp => {
122+ } ) . then ( async resp => {
123+ const hashcashChallenge = resp . headers . get ( 'X-Hashcash' )
124+ if ( hashcashChallenge ) {
125+ json . _hashcash = await generateHashcashToken ( hashcashChallenge )
126+ // Simulate an EAGAIN response
127+ return - 3
128+ }
129+ return handleApiResponse ( resp )
130+ } ) . then ( resp => {
107131 if ( this . closed && ! isLogout ) return
108132 if ( ! resp ) return cb ( Error ( 'Empty response' ) )
109133
@@ -203,15 +227,13 @@ class API extends EventEmitter {
203227 }
204228
205229 static getShouldAvoidUA ( ) {
206- // Checks defined using
207- // - https://codepen.io/qgustavor/pen/JjqqBPp
208- // - https://www.browserstack.com/screenshots/149d6d45a4a10de06e05f743190a4c12a9faa6ef
209-
210230 // It's not possible to detect when a browser fails CORS requests by defining an user-agent
211231 // using feature detection, so the alternatives were using user-agent detection
212232 // (which is not ideal because might not catch Firefox forks) or hacks.
213233
214- // This library uses hacks:
234+ // This library uses hacks.
235+ // Those were found using BrowserStack and the following tests: https://codepen.io/qgustavor/pen/JjqqBPp
236+
215237 let headersErr
216238 try {
217239 globalThis . Headers ( )
0 commit comments