- Create local and remote-bound requests
- Built-in support for
http:,https:,file:, anddata:protocols - Auto-parse JSON responses, no need for
JSON.parse/try/catchpost-callback - Saves bandwidth by supporting requests that have multiple levels of compression (
Accept-Encoding) - Built-in checksum generator, generate checksums on the data as the request is being received
- Custom
enginessupport, bring your own support forhttp:,https:,file:,data:, and other protocols that jetta does not currently support - Convenient request body handlers for forms, JSON, and streams
- Makes minimal assumptions - returns the data so that you may decide what to do with it
- Example:
jetta.request('altusaero.com', (error, results) => {
// for convenience purposes, 'results.error' is the same as the 'error' param
console.dir(results, {colors: true})
})jetta.request* represents both jetta.request and jetta.requestPromise throughout this document
-
jetta.request(
urlOBJECT | STRING[,optionsOBJECT],callbackFUNCTION)-
All options are optional (defaults can be found in
jetta.defaults.request) -
Uses
jetta.urlParserinternally, which has Internationalized Domain Name (IDN) support -
Automatically decompresses data by default. See Decompression and Encodings for more information.
-
urlOBJECT | STRING - the URL for the request- Will be parsed via
jetta.urlParser - Can be set to
nullif essential request information, such as hostname and protocol, are provided viaoptions.requestOptions
- Will be parsed via
-
optionsOBJECT optional-
Options for the request
-
For your convenience the following options merge with the defaults, rather than completely overwriting them:
checksumenginesredirectsPreserveHeaderrequestOptionsrequestOptions.headerssecureProtocolsurlParser- Example:
const checksum = { algorithm: 'sha384' } jetta.request('altusaero.com', {checksum}, (error, results) => { assert(results.options.checksum.algorithm, checksum.algorithm) assert(results.options.checksum.digest, 'hex') console.log('checksum option merge ok') })
-
agentsOBJECT |null- a key-value object of agents to use for the request.- The key is the protocol, including colon, while the value is the agent for that protocol.
- Example:
const agents = { 'http:': new http.Agent(), 'https:': new https.Agent() } jetta.request('altusaero.com', {agents}, (error, results) => {/*...*/})
-
bodyBUFFER |null- a body to send with the request- Sets
options.requestOptions.methodto 'POST' if no method has been set - Determines and sets
options.requestOptions.headers['Content-Length']if the header has not been set - Not recommended for use with
options.form,options.json, oroptions.requestBodyStream
- Sets
-
cookieOBJECT |null- a key-value object to generate a Cookie header for the request- Updates
options.requestOptions.headers.Cookie - Example:
const cookie = { 'id': '1503007806947-0-10592-example-0543adbfc6c7836b8963-2a', 'lang': 'en' } jetta.request('altusaero.com', {cookie}, (error, results) => {/*...*/})
- Updates
-
cookieManagerCOOKIEMANAGER |null- ajetta.CookieManagerinstance- Used to handle cookies for the request and any redirects
- Useful for creating an enduring cookie storage
- Waits for manager to be ready, if not ready
- Using this option overwrites
options.cookieandoptions.requestOptions.headers.Cookie - Example:
const cookieManager = new jetta.CookieManager() jetta.request('altusaero.com', {cookieManager}, (error, results) => {/*...*/})
- You can create a 'disposable'
jetta.CookieManagerinstances for enhanced privacy by creating unique instances per request - See cookie-manager.md for more information
-
checksumOBJECT |null- generates a checksum on the received data from the request-
Works will all protocols, including
data:andfile: -
The resulting checksum will be available via
results.checksum -
Can be used in conjunction with other options, including
onResponseData,toFile, andstoreDataInResults. For example, setstoreDataInResultstofalseif you're only interested in receiving the checksum of the data, but not the data itself. -
algorithmSTRING - a hash algorithm supported by Node.js- Run
crypto.getHashes()to get supported list
- Run
-
digestSTRING optional - the method to digest the checksum buffer.- Usually set to 'hex' or 'base64'.
-
expectedSTRING optional - the expected result- If used will throw an error if the checksums do not match.
-
Naturally, checksums are not supported on encoded data - using this option and enabling non-supported encodings may result in an error. See Decompression and Encodings for details.
-
-
dataLimitINTEGER - the maximum amount of data received before aborting the request- This is considered before and after any decompression is used - to ensure protection against zip bombs
-
enginesOBJECT |null- a key-value object of the custom engines to use for the request-
The key is the protocol, including colon, while the value is the function used to create the request.
-
Useful for adding support for unsupported protocols in jetta, or modifying existing ones
- Such as using Electron's net module for extended proxy support
-
The function's API should match Node.js's
http.requestfunctionality -
Example:
engines: { 'http:': http.request, 'https:': specialHTTPSRequestFunction, 'super-new-protocol:': superCoolNewProtocol } jetta.request('super-new-protocol://here/we/go', {engines}, (error, results) => {/*...*/})
-
This can be added directly to jetta's test suite for convenient compatibility checking. See ../test/README.md for details.
-
-
formOBJECT |null- a form body to convert to a querystring and send with the request- Sets
options.requestOptions.headers['Content-Type']to 'application/x-www-form-urlencoded' if the header has not been set - Sets
options.requestOptions.methodto 'POST' if no method has been set - Determines and sets
options.requestOptions.headers['Content-Length']if the header has not been set - Not recommended for use with
options.body,options.json, oroptions.requestBodyStream
- Sets
-
jsonOBJECT |null- a JSON body to convert to stringify and send with the request- Sets
options.requestOptions.headers['Content-Type']to 'application/json' if the header has not been set - Sets
options.requestOptions.methodto 'POST' if no method has been set - Determines and sets
options.requestOptions.headers['Content-Length']if the header has not been set - Not recommended for use with
options.body,options.form, oroptions.requestBodyStream
- Sets
-
onResponseData(data BUFFER, results OBJECT) - streams data as it is received from the request- Useful for streaming and reducing RAM usage
- May be called multiple times as data is being received
- Works with all protocols, including
data:andfile: datais decompressed, except where 'Accept-Encoding' is non-default- See Decompression and Encodings for more information on this
- While asynchronous, this will be guaranteed to be called before the main callback if there is any data to be received/processed. This way you will not have to worry or plan for missing data.
- Can check the current state of the
resultsso that you can make decisions on the received data, such as:- Checking
results.dataEncoding- Useful custom decoding support, such as 'brotli'
- See Decompression and Encodings for more information on this functionality
- Checking
results.redirectsfor any redirects (may want to empty the existing payload if there was a redirect)
- Checking
- On end the main callback will be called, whether an error occurred or not
- Can be used in conjunction with
options.storeDataInResultsandoptions.toFile - See Readable Response Streams vs
onResponseData
-
preferredErrorLanguageSTRING - as an ISO 639-1 code- See
jetta.JettaErrorfor details
- See
-
redirectLimitINTEGER - the max number of redirects before aborting the request -
redirectsPreserveHeaderOBJECT |null- determines which headers are preserved on redirect- Can be conveniently set as
ALWAYS,SAMESITE, orNEVERviajetta.request.constants.redirectsPreserveHeader - Example:
const {ALWAYS, SAMESITE, NEVER} = jetta.request.constants.redirectsPreserveHeader const redirectsPreserveHeader = { Accept: ALWAYS, Authorization: SAMESITE, DNT: ALWAYS } jetta.request('altusaero.com', {redirectsPreserveHeader}, (error, results) => {/*...*/})
- Notes for the following headers:
- Can be conveniently set as
-
redirectsUpdateRefererBOOLEAN - determines if jetta should update the Referer header on redirects- See 'Referer header resolution' in Redirects section
- Read more about the pros/cons of the
Refererheader from the RFC 7231 Section 5.5.2. Here are a few:- Pros:
- Helps prevent CSRF attacks
- Required for some sites to verify logins
- Cons:
- Reveals request browsing history (privacy)/tracking
- Some sites may deny links from other sites
- Pros:
-
requestOptionsOBJECT |null- the request options to use for the native request- All Node.js request options are supported. See the API docs for the complete list.
- The
requestOptions.headersobject will be separately merged so that useful defaults may persist
-
requestBodyStreaminstanceofstream.Readable|null- a readable stream to send to the server- Useful for large uploads, whether it be MBs, GBs, or TBs of data
- Sets
options.requestOptions.headers['Transfer-Encoding']to 'chunked'` if the header has not been set - Sets
options.requestOptions.methodto 'POST' if no method has been set - Not recommended for use with
options.body,options.form, oroptions.json
-
secureProtocolsOBJECT |null- a key-value list of protocols, with their colon, that are deemed 'secure'- The value must be set to
true. Setting tofalseoverwrites the defaults for that particular protocol. - Used internally for redirects, cookies, and Referer header. See Redirects for more information.
- Example:
const secureProtocols = { 'https:': true } jetta.request('altusaero.com', {secureProtocols}, (error, results) => {/*...*/})
- The value must be set to
-
storeDataInResultsBOOLEAN - determines if the data to be concatenated and stored in theresultsobject- A few use cases where this may be set to
false:- If you do not want the data to be stored in RAM
options.onResponseDataoroptions.toFilehave been used (to create a stream-only request)- For large requests that may not fit in RAM all at once and
options.dataLimitis set to a high value relative to available RAM - Not interested in the response payload itself, but rather if the request succeeded or not
- Setting to
falsedisables automatic JSON-parsing - If
truethe data inresults.datais decompressed by default. See Decompression and Encodings for more information.
- A few use cases where this may be set to
-
timeLimitINTEGER - the time limit, in milliseconds, before the request times out- A timeout can occur during the request initialization or if no data has been received
-
toFileSTRING | instanceofurl.URL|null- saves the response data to a file- Streams to a file as the data is received - no worries on memory usage
- Perfect for large downloads
- Can be used in conjunction with
onResponseDataandstoreDataInResults - If instanceof
url.URL, must use thefile:protocol - If any redirects, will use last request
- Will overwrite an existing file
- Streams to a file as the data is received - no worries on memory usage
-
urlParserOBJECT |null- options to pass tojetta.urlParserwhen processing URLs- Example:
const urlParser = { "addMissingProtocol": true } jetta.request('altusaero.com', {urlParser}, (error, results) => {/*...*/})
- Example:
-
-
callback(errorinstanceofjetta.JettaError|null,resultsOBJECT)- The completed request, where
erroris an instance ofjetta.JettaError - See Response Results for the values in the
resultsobject
- The completed request, where
-
-
jetta.request.constantsOBJECT-
An object with constant objects and values
-
Should rely on the constant's key, not value, as the value may change in the future
-
redirectsPreserveHeaderOBJECTALWAYSINTEGER- Denotes that the the header set with this value should always be set on redirects
NEVERINTEGER- Denotes that the the header set with this value should never be set on redirects
SAMESITEINTEGER- Denotes that the the header set with this value should be set on redirects if it is a same-site redirect
-
results is an object that always contain the following, even from options.onResponseData:
results.checksumSTRING |null- the checksum generated, ifoptions.checksumhas been usedresults.dataBUFFER |null- the response body- Always
nullifoptions.storeDataInResultsis set tofalse
- Always
results.dataEncodingARRAY |nullresults.errorinstanceofjetta.JettaError|null- an error with the request,nullif no error occurred- Same value as the 'error' parameter from the callback
results.jsonOBJECT |null- the parsed JSON response body- Works natively for the
data:,http:, andhttps:protocols where the proper content-type is available - Always
nullifoptions.storeDataInResultsis set tofalse
- Works natively for the
results.lengthsOBJECTresults.lengths.contentNUMBER- The Content-Length header value as a Number
- May be a valid number or
NaN
results.lengths.dataINTEGER- The length of the received data
- Always a safe integer, even if an error has occurred
results.lengths.decompressedINTEGER- The amount of data decompressed
- Always a safe integer, even if an error has occurred
results.lengths.responseINTEGER- The amount of data received from the response payload, usually compressed, which means this may be lower than
results.lengths.data - Useful for comparing compression ratios
- Always a safe integer, even if an error has occurred
- The amount of data received from the response payload, usually compressed, which means this may be lower than
results.optionsOBJECT- The normalized options used.
results.options.checksumOBJECT - nevernullresults.options.enginesOBJECT - nevernullresults.options.redirectsPreserveHeaderOBJECT - nevernullresults.options.requestOptionsOBJECT - nevernullresults.options.secureProtocolsOBJECT - nevernullresults.options.urlParserOBJECT - nevernull
results.redirectsARRAY- The results from previous redirects in the order in which they were received
- Useful for tracing and debugging
- Every object in
results.redirectsis a validresultsobject
results.responseHeadersOBJECT |null- the headers from the response, lowercased internally by Node.jsresults.statusCodeNUMBER - the status code of the response- May be
NaN
- May be
results.timeOBJECT - nevernullresults.time.totalINTEGER- The total request time in milliseconds
- Available even if an error has occurred
- Always a safe integer
results.time.requestNUMBER- The total time for the most recent request in milliseconds
- 'Most recent' meaning if there were any redirects, this is the time for the last redirect
- May be
NaN
- The total time for the most recent request in milliseconds
results.time.requestResponseNUMBER- The total response time for the most recent request in milliseconds
- If is a safe integer, always equal to or less than
results.time.request - May be
NaN
results.urlOBJECT- The results from
jetta.urlParser results.url.isLocalhostBOOLEANresults.url.isValidBOOLEANresults.url.optionsOBJECT - nevernullresults.url.parsedURLOBJECT - nevernullresults.url.urlSTRING
- The results from
- Final callback is always called once.
- An
erroris always an instance ofjetta.JettaErrorornull. urlandoptionsare never mutated- Except for
options.cookieManager, which is natural as it updates your cookies
- Except for
- Always returned asynchronously, even if results are immediately available (such as
data:requests) resultsas defined in the Response Results section- Note that
options.requestStreammay fail, as it would have been streamed - 'auth' attribute from redirect's URL
- Authorization header from URL that received the redirect (if
options.redirectsPreserveHeaderoption allows) - 'auth' attribute from URL that received the redirect sameSite redirect
- None
- None, if any of the following are true:
- If redirecting from localhost
- Original protocol was
data: - Original protocol was
file: - If redirecting from a secure protocol to a non-secure protocol (as determined by the
secureProtocolsoption)
- If
options.redirectsUpdateRefereris set totrue- Use, removing any auth (user:pass) & hash (#hash) from the URL
- Referer header from URL that received the redirect (if
options.redirectsPreserveHeaderoption allows) - None
- Retains requestOptions.socketPath, if used
- Retains
options.cookieoption, if usedoptions.cookieManageris kept regardless as it has for request context
- It complicates redirects. For example, how should
readable.pipe(...)work? - Handling requests without a response body is a bit more straight-forward
- Simplicity/compatibility with callback and promise-based
jetta.request* - Using streams makes other build-in features, such as decompression and checksums, more complicated as it would have to be double-piped and checking for errors in more places
- Perhaps an
options.onResponseStreamAvailablethat returns a readable stream per internal request would suffice
jetta.request* automatically decompresses data by default, including requests with multiple values for their Content-Encoding headers. By default unsupported encodings result in an error. This is done so that users do not have to perform additional checks and guesswork on the response data - one may safely use options.checksum, options.onResponseData, options.toFile, results.data, and results.json without a second thought.
To accept unsupported encodings set the 'Accept-Encoding' header in options.requestOptions.header accordingly. If Accept-Encoding contains a non-supported encoding, such as 'brotli', and the server sends that encoding in the response jetta will not perform any decoding and will set results.dataEncoding to an array of encodings (strings) in order of which they were applied from the server.
We've created and test for a list of guarantees so that you may always have consistent, reliable data:
If we ever need to change these guarantees, we will issue a major release. See ../test/README.md for how we accomplish and maintain these guarantees.
jetta.request uses jetta.domainLib internally to determine if a site is 'same site' (mainly for redirect purposes)
Sometimes redirects can be a bit tricky to determine handle and predict. This section should shed some light on areas where things are usually complicated.
Status codes 307 & 308 carry request body (options.body, options.form, options.json, options.requestStream) through redirect
Auth (Authorization) preferred order:
Referer header resolution:
If samesite redirect:
If using request headers 'If-Modified-Since' or 'If-None-Match' you may receive 'jetta-request-bad-response-code' with a 304 error. If this happens, the server is telling you to 'use existing cache' as described by the header provided. This isn't necessary an 'error', but it is clearer to understand than for jetta.request* to not return an error and for you to perform guesswork.
We use options.onResponseData over returning a readable stream for the following reasons:
Our primary goals with jetta is to make requests simple to create, data easy to digest, and debugging super simple. While we have a list of drawbacks, we are not totally closed to the idea of a response stream and are open to suggestions.