|
|
|
import {Injectable, ScopedContainer, Container} from "@extollo/di"
|
|
|
|
import {infer} from "@extollo/util"
|
|
|
|
import {IncomingMessage, ServerResponse} from "http"
|
|
|
|
import {HTTPCookieJar} from "../kernel/HTTPCookieJar";
|
|
|
|
import {TLSSocket} from "tls";
|
|
|
|
import * as url from "url";
|
|
|
|
import {Response} from "./Response";
|
|
|
|
import {ActivatedRoute} from "../routing/ActivatedRoute";
|
|
|
|
|
|
|
|
// FIXME - add others?
|
|
|
|
export type HTTPMethod = 'post' | 'get' | 'patch' | 'put' | 'delete' | 'unknown';
|
|
|
|
export function isHTTPMethod(what: any): what is HTTPMethod {
|
|
|
|
return ['post', 'get', 'patch', 'put', 'delete'].includes(what)
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface HTTPProtocol {
|
|
|
|
string: string,
|
|
|
|
major: number,
|
|
|
|
minor: number,
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface HTTPSourceAddress {
|
|
|
|
address: string;
|
|
|
|
family: 'IPv4' | 'IPv6';
|
|
|
|
port: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class Request extends ScopedContainer {
|
|
|
|
|
|
|
|
public readonly cookies: HTTPCookieJar;
|
|
|
|
|
|
|
|
public readonly url: string;
|
|
|
|
public readonly fullUrl: string;
|
|
|
|
public readonly method: HTTPMethod;
|
|
|
|
public readonly secure: boolean;
|
|
|
|
public readonly protocol: HTTPProtocol;
|
|
|
|
public readonly path: string;
|
|
|
|
public readonly rawQueryData: {[key: string]: string | string[] | undefined};
|
|
|
|
public readonly query: {[key: string]: any};
|
|
|
|
public readonly isXHR: boolean;
|
|
|
|
public readonly address: HTTPSourceAddress;
|
|
|
|
public readonly response: Response;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
protected clientRequest: IncomingMessage,
|
|
|
|
protected serverResponse: ServerResponse,
|
|
|
|
) {
|
|
|
|
super(Container.getContainer())
|
|
|
|
|
|
|
|
this.secure = !!(clientRequest.connection as TLSSocket).encrypted
|
|
|
|
|
|
|
|
this.cookies = new HTTPCookieJar(this)
|
|
|
|
this.url = String(clientRequest.url)
|
|
|
|
this.fullUrl = (this.secure ? 'https' : 'http') + `://${this.getHeader('host')}${this.url}`
|
|
|
|
|
|
|
|
const method = clientRequest.method?.toLowerCase()
|
|
|
|
this.method = isHTTPMethod(method) ? method : 'unknown'
|
|
|
|
|
|
|
|
this.protocol = {
|
|
|
|
string: clientRequest.httpVersion,
|
|
|
|
major: clientRequest.httpVersionMajor,
|
|
|
|
minor: clientRequest.httpVersionMinor,
|
|
|
|
}
|
|
|
|
|
|
|
|
this.register(Request)
|
|
|
|
this.instances.push({
|
|
|
|
key: Request,
|
|
|
|
value: this,
|
|
|
|
})
|
|
|
|
|
|
|
|
const parts = url.parse(this.url, true)
|
|
|
|
|
|
|
|
this.path = parts.pathname ?? '/'
|
|
|
|
this.rawQueryData = parts.query
|
|
|
|
|
|
|
|
const query: {[key: string]: any} = {}
|
|
|
|
for ( const key in this.rawQueryData ) {
|
|
|
|
const value = this.rawQueryData[key]
|
|
|
|
|
|
|
|
if ( Array.isArray(value) ) {
|
|
|
|
query[key] = value.map(x => infer(x))
|
|
|
|
} else if ( value ) {
|
|
|
|
query[key] = infer(value)
|
|
|
|
} else {
|
|
|
|
query[key] = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.query = query
|
|
|
|
this.isXHR = String(this.clientRequest.headers['x-requested-with']).toLowerCase() === 'xmlhttprequest'
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
const {address = '0.0.0.0', family = 'IPv4', port = 0} = this.clientRequest.connection.address()
|
|
|
|
this.address = {
|
|
|
|
address,
|
|
|
|
family,
|
|
|
|
port
|
|
|
|
}
|
|
|
|
|
|
|
|
this.response = new Response(this, serverResponse)
|
|
|
|
}
|
|
|
|
|
|
|
|
public getHeader(name: string) {
|
|
|
|
return this.clientRequest.headers[name.toLowerCase()]
|
|
|
|
}
|
|
|
|
|
|
|
|
public toNative() {
|
|
|
|
return this.clientRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
public input(key: string) {
|
|
|
|
if ( key in this.query ) {
|
|
|
|
return this.query[key]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// session
|
|
|
|
// route
|
|
|
|
// respond
|
|
|
|
// body
|
|
|
|
// hostname
|
|
|
|
|
|
|
|
/*
|
|
|
|
param
|
|
|
|
json
|
|
|
|
fresh/stale - cache
|
|
|
|
remote ips (proxy)
|
|
|
|
signedCookies
|
|
|
|
accepts content type, charsets, encodings, languages
|
|
|
|
is content type (wants)
|
|
|
|
range header parser
|
|
|
|
*/
|
|
|
|
}
|