rxhr
Version:
Tiny Observable based HTTP client
98 lines (86 loc) • 3.16 kB
JavaScript
import $$observable from 'symbol-observable'
const encodeParams = params => Object.keys(params).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&')
const buildUrl = (url, params) => params ? url + '?' + encodeParams(params) : url
const fromResponseHeaderString = (headersString) => {
const headers = {}
headersString.split('\n').forEach(line => {
const index = line.indexOf(':')
if (index > 0) headers[line.slice(0, index)] = line.slice(index + 1).trim()
})
return headers
}
const noop = () => {}
const rxhr = options => {
return {
subscribe (onNext, onError, onComplete) {
let observer = onNext
let request = new XMLHttpRequest()
if (typeof onNext === 'function') {
observer = {
next: onNext,
error: onError || noop,
complete: onComplete || noop
}
}
try {
const buildResponse = (err) => {
const body = err || (!options.responseType || options.responseType === 'text' ? request.responseText : request.response)
let response = {
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
status: request.status === 1223 ? 204 : request.status,
ok: request.status >= 200 && request.status < 300,
type: err ? 'error' : 'default',
statusText: err ? request.statusText : (request.statusText || 'OK'),
headers: fromResponseHeaderString(request.getAllResponseHeaders()),
url: request.responseURL,
text: () => typeof body === 'object' ? JSON.stringify(body) : body,
json: () => typeof body === 'string' ? JSON.parse(body) : body,
blob: () => new Blob([body])
}
return response
}
const onReqLoad = () => {
let response = buildResponse()
if (response.ok) {
observer.next(response)
observer.complete()
return
}
observer.error(response)
}
const onReqError = () => {
const response = buildResponse(new Error('Network Error'))
observer.error(response)
}
const onReqTimeout = () => {
const response = buildResponse(new Error('ECONNABORTED'))
observer.error(response)
}
request.open(options.method.toUpperCase(), buildUrl(options.url, options.params))
// response type
options.responseType && (request.responseType = options.responseType)
// with credentials
request.withCredentials = options.withCredentials === true
// headers
for (let i in options.headers) request.setRequestHeader(i, options.headers[i])
// timeout in ms
request.timeout = options.timeout
request.send(options.body || null)
request.onload = onReqLoad
request.onerror = onReqError
request.ontimeout = onReqTimeout
} catch (err) {
observer.error(err)
}
return {
unsubscribe () {
request.abort()
}
}
},
[$$observable] () {
return this
}
}
}
export default rxhr