Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1070 lines (933 sloc) 32.172 kB
var http = require('http');
var https = require('https');
var url = require('url');
var fs = require('fs');
var formidable = require('formidable');
var server_debug = false
/** @externType time_t */
/** @externType caml_list('a) */
/** @externType endpoint */
/** @externType llarray('a) */
/** @externType binary */
/** @externType WebInfo.private.native */
/** @externType WebInfo.private.native_http_header */
/** @externType WebInfo.private.native_request */
/** @externType WebInfo.private.native_connection */
/** @externType WebInfo.private.native_response */
/** @externType web_server_status */
/** @externType HttpRequest.msg_list */
/** @externType HttpRequest.multipart */
/** @externType list('a) */
/** @opaType HttpRequest.part */
/** @opaType tuple_3('a, 'b, 'c) */
/** @externType Server.private.options */
function DummyOpaResponse(at_end){
this.is_dummy = true;
this.at_end = at_end;
this.content = new Array();
}
DummyOpaResponse.prototype = {
writeHead:function(){},
setHeader:function(){},
end:function(chunk){
if(typeof chunk != 'undefined'){
this.content.push(chunk)
}
this.at_end(this.content.join(""))
}
}
/** @module ConvertHeader */
function merge_headers(headers) {
var hdr = { };
headers.forEach(function (h) { for (var f in h) { hdr[f] = h[f]; } });
return hdr
}
/**
* @register {string -> WebInfo.private.native_http_header}
*/
function set_cookie(value) {
return { 'Set-Cookie': value }
}
/**
* @register {time_t -> WebInfo.private.native_http_header}
*/
function last_modified(date) {
var now = new Date(date)
return { 'Last-Modified': now.toUTCString() }
}
/**
* @register {string -> WebInfo.private.native_http_header}
*/
function cache_control(value) {
return { 'Cache-Control': value }
}
/**
* @register {string -> WebInfo.private.native_http_header}
*/
function pragma(value) {
return { 'Pragma': value }
}
/**
* @register {string -> WebInfo.private.native_http_header}
*/
function location(value) {
return { 'Location': value }
}
/**
* @register {string -> WebInfo.private.native_http_header}
*/
function cdisp_attachment(value) {
return { 'Content-Disposition': "attachment; filename="+value+";" }
}
/**
* @register {opa[option(time_t)] -> WebInfo.private.native_http_header}
*/
function expires_at(t) {
var exp_time = new Date();
if ('some' in t) {
exp_time.setTime(exp_time.getTime()+t.some);
} else {
exp_time.setFullYear(exp_time.getFullYear()+1);
};
return { 'Expires': exp_time.toUTCString() }
}
/**
* @register {string, string -> WebInfo.private.native_http_header}
*/
function custom(t, v) {
var h = {};
h[t] = v;
return h;
}
/** @endModule */
/** @externType SSL.private_key */
/** @externType SSL.policy */
/** @externType SSL.certificate */
/** @externType SSL.secure_type */
/** @module ssl */
/**
* @register {opa[option(string)], opa[option(string)], string, string, string -> SSL.private_key}
*/
function make_key(ca_file, ca_path, cert_file, cert_key, cert_pass) {
return {
ca_file : option2js(ca_file),
ca_path : option2js(ca_path),
cert_file : cert_file,
cert_pass : cert_pass,
cert_key : cert_key
}
}
/**
* @register {opa[option(string)], opa[option(SSL.certificate -> bool)], bool, string, string, string -> SSL.policy}
*/
function make_policy(client_ca_file, fallback, always, ca_file, ca_path, cert_path) {
return {
client_ca_file : option2js(client_ca_file),
fallback : option2js(fallback),
always : always,
ca_file : ca_file,
ca_path : ca_path,
cert_path : cert_path
}
}
/**
* @register {opa[option(SSL.private_key)], opa[option(SSL.policy)] -> SSL.secure_type}
*/
function make_secure_type(key, policy) {
return {
key : option2js(key),
policy : option2js(policy)
};
}
/** @endModule */
/** @module Http_server */
/**
* @register {WebInfo.private.native -> WebInfo.private.native_request}
*/
function web_info_request(req) {
// We'll just leave it intact, it doesn't have any internal "request" field,
// the whole thing is the request.
// We also have to do it this way to ensure that the extra parameters are in the
// native_request rather than in the native.
return req
}
/**
* @register {WebInfo.private.native -> (WebInfo.private.native_response -> void)}
*/
function web_info_cont(req) {
// We may need to add extra fields in the Opa handling
return function (resp) { return js_void }
}
/**
* @register {WebInfo.private.native -> WebInfo.private.native_connection}
*/
function web_info_conn(req) {
// The connection is actually in the request
return req.request.connection
}
/**
* @register {(WebInfo.private.native_response -> void), \
WebInfo.private.native_request, \
WebInfo.private.native_connection -> WebInfo.private.native}
*/
function web_info_reconstruct(f, r, c) {
//let web_info_reconstruct f r c = {
// HttpServerTypes.cont = f; -- not present
// HttpServerTypes.request = r; -- the whole request object
// HttpServerTypes.connection = c; -- ignore this, it's in the request
// HttpServerTypes.certificate = None; -- use request.connection.getPeerCertificate etc.
//}
return r
}
/**
* @register {WebInfo.private.native_request -> string}
*/
function get_user_agent(req) {
if ('user-agent' in req.request.headers)
return req.request.headers['user-agent']
else if ('User-Agent' in req.request.headers)
return req.request.headers['User-Agent']
else
return "unknown"
}
/**
* @register {WebInfo.private.native_request -> bool}
*/
function is_apple_mobile_webapp(req) {
var ua = get_user_agent(req);
return ua.indexOf("Mobile",0) != -1 && ua.indexOf("Apple",0) != -1 && ua.indexOf("Safari",0) == -1
}
/**
* @register {WebInfo.private.native_request -> bool}
*/
function is_apple_mobile(req) {
var ua = get_user_agent(req);
return ua.indexOf("Mobile",0) != -1 && ua.indexOf("Apple",0) != -1
}
// Not present, will need to be injected into prototype when createServer is called
/**
* @register {WebInfo.private.native_request -> string}
*/
function get_server_url(req) {
return req.url;
}
/**
* @register {WebInfo.private.native_request -> string}
*/
function get_uri(req) {
return req.request.url
}
/**
* @register {WebInfo.private.native_request -> string}
*/
function get_method(req) {
return req.request.method
}
/**
* @register {WebInfo.private.native_request -> bool}
*/
function is_secured(req) {
// just guesswork
var cert = req.request.connection.getPeerCertificate()
return cert !== null && typeof cert != 'undefined';
}
/**
* @register {WebInfo.private.native_request -> opa[list(string)]}
*/
function get_header_names(req) {
var lst = [];
for (var fld in req.request.headers) { lst.push(fld) };
return js2list(lst)
}
function gethdr(request, s) {
return (s in request.headers) ? {some:request.headers[s]} : js_none
}
/**
* @register {WebInfo.private.native_request -> (string -> opa[option(string)])}
*/
function get_header_values(req) {
return function (s) { return gethdr(req.request, s) }
}
/**
* @register {WebInfo.private.native_connection -> string}
*/
function ip_of_web_info(c) {
return c.remoteAddress
}
/**
* @register {WebInfo.private.native_connection -> bool}
*/
function check_connection(c) {
// Just have to assume open
return true
}
// BslNet.Http_server.make_response
function is_valid(request, modified_since) {
if (request.headers['cache-control'] === 'no-cache')
return false
else if (request.headers['pragma'] === 'no-cache')
return false
else if ('if-modified-since' in request.headers) {
if (is_some(modified_since)) {
var mtimein = new Date(request.headers['if-modified-since']);
return mtimein >= modified_since.some
} else false
} else if ('if-unmodified-since' in request.headers) {
if (is_some(modified_since)) {
var mtimein = new Date(request.headers['if-unmodified-since']);
return mtimein < modified_since.some
} else false
} else false
}
function process_content(req, expires, modified_since, _type, content, content_len, include_body) {
var now = new Date();
var content_len = dflt(content.length,content_len); // TODO: how do we get the real length of one of these things?
//if (server_debug) console.log("content_len="+content_len);
var cs;
if (_type.indexOf("charset") != -1) { cs = "" } else { cs = "; charset=utf-8" };
var typeval = _type + cs;
var expires = dflt(new Date(), expires);
req.response.setHeader('Date',now.toUTCString());
req.response.setHeader('Server',req.server_name);
req.response.setHeader('Content-Type',typeval);
req.response.setHeader('Expires',expires.toUTCString());
if (is_some(modified_since)) {
req.response.setHeader('Cache-Control',"public");
req.response.setHeader('Last-Modified',modified_since.some);
};
// req.response.setHeader('Content-Length',content_len);
if (!req.response.is_dummy) cookies_out(req, false);
if (include_body)
req.response.end(buffer_of_binary(content));
else
req.response.end();
}
function make_response_with_headers(expires,modified_since,req,headers_out,status_line,_type,content,content_len,cont) {
var code = BslNet_Requestdef_status_code(status_line);
var reason = BslNet_Requestdef_reason_phrase(code);
var include_body = req.request.method !== 'HEAD';
if (server_debug) console.log("make_response: _type="+_type);
if (is_valid(req.request, modified_since)) {
if (server_debug) console.log("make_response: code="+304);
req.response.writeHead(304, {'Date': new Date().toUTCString()});
req.response.end('Not Modified');
} else {
if (server_debug) console.log("make_response: code="+code);
req.response.statusCode = code;
headers_out.forEach(function (hdr) { for (var h in hdr) { req.response.setHeader(h,hdr[h]) } });
process_content(req, expires, modified_since, _type, content, content_len, include_body)
};
return_(cont, req);
return;
}
/**
* @register {opa[option(time_t)], WebInfo.private.native_request, \
web_server_status, \
caml_list(WebInfo.private.native_http_header), \
string, binary, opa[option(int)], \
continuation(WebInfo.private.native_response) -> void}
* @cpsBypass
*/
function make_response_len(ms,req,stat,headers,_type,content,content_len,k) {
var modified_since = map(function (ms) { return new Date(ms) },ms);
make_response_with_headers(js_none, modified_since, req, headers, stat, _type, content, content_len, k);
}
/**
* @register {opa[option(time_t)], WebInfo.private.native_request, \
web_server_status, \
caml_list(WebInfo.private.native_http_header), \
string, binary, \
continuation(WebInfo.private.native_response) -> void}
* @cpsBypass
*/
function make_response(ms,req,stat,headers,_type,content,k) {
var modified_since = map(function (ms) { return new Date(ms) },ms);
make_response_with_headers(js_none, modified_since, req, headers, stat, _type, content, js_none, k);
}
/**
* @register {opa[option(time_t)], WebInfo.private.native_request, \
web_server_status, string, binary, \
continuation(WebInfo.private.native_response) -> void}
* @cpsBypass
*/
function make_response_modified_since(modified_since,req,stat,_type,content,k) {
var expires = {some:new Date(0)};
var modified_since = map(function (ms) { return new Date(ms) }, modified_since);
make_response_with_headers(expires, modified_since, req, [], stat, _type, content, js_none, k);
}
/**
* @register {opa[option(time_t)], opa[option(time_t)], \
WebInfo.private.native_request, web_server_status, string, \
binary, continuation(WebInfo.private.native_response) -> void}
* @cpsBypass
*/
function make_response_expires_at(expires_at,modified_since,req,stat,_type,content,k) {
var date = new Date();
var expires = (is_some(expires_at)) ? date.setTime(expires_at.some) : date.setFullYear(date.getFullYear()+1);
var modified_since = map(function (ms) { return new Date(ms) },modified_since);
make_response_with_headers(expires, modified_since, req, [], stat, _type, content, js_none, k);
}
http._opa_servers = {};
/**
* @register {opa[option(string)] -> int}
*/
function get_port(name) {
var name = option2js(name);
name = name ? name : "default";
var port = http._opa_servers[name].port;
return port ? port : -1;
}
/**
* @register {opa[option(string)] -> string}
*/
function get_addr(name) {
// TODO
return "0.0.0.0";
}
var has_init_server = false;
function opt_time(ot) {
return (typeof ot.seconds == 'undefined') ? Time_infinity : ot.seconds;
}
/**
* @register {string, Server.private.options, opa[option(string)], opa[option(string)], \
opa[option(string)], SSL.secure_type, \
(WebInfo.private.native -> void), \
(string, HttpRequest.msg_list, int -> bool) \
-> void}
*/
function init_server(name,options,certfileo,privkeyo,passwdo,
secure_type,dispatcher,ontransfer) {
var server;
var is_s = false;
var server_callback = function (request, response) {
var pathname = url.parse(request.url).pathname;
increment_cookie_connection_count();
var req = cookies_in({timestamp:new Date(), pathname:pathname,
server_name:name, cookies:{},
request:request, response:response});
//if (debug) console.log(req);
return dispatcher(req)
}
var privkeyo = option2js(privkeyo);
var passwdo = option2js(passwdo);
var certfileo = option2js(certfileo);
if (secure_type.key != null){
var sopt = {};
var skey = secure_type.key;
if (skey != null){
var ca_path = "";
if (skey.ca_path) ca_path = skey.ca_path + "/";
sopts.key = fs.readFileSync(path + skey.cert_key);
sopts.passphrase = skey.cert_pass;
sopts.cert = fs.readFileSync(path + skey.cert_file);
}
server = https.createServer(sopts, server_callback);
is_s = true;
} else if (privkeyo != null && passwdo != null && certfileo != null) {
var sopts = {passphrase:passwdo};
sopts.key = fs.readFileSync(privkeyo);
sopts.cert = fs.readFileSync(certfileo);
server = https.createServer(sopts, server_callback);
is_s = true;
} else {
server = http.createServer(server_callback);
}
opts = cookies_default_opts;
opts.gc_period = options.cookie_gc_period;
opts.accept_client_values = options.cookie_accept_client_values;
opts.pool_min = options.cookie_pool_size_min;
opts.pool_max = options.cookie_pool_size_max;
opts.timer_interval = options.timer_interval;
opts.rate_max = options.cookie_rate_max;
opts.dt1 = opt_time(options.dt1);
opts.dt2 = opt_time(options.dt2);
opts.cookies_filename = options.cookies_filename;
server.listen(options.port, function(){
var pro = is_s ? "https" : "http";
var cap_name = name.charAt(0).toUpperCase() + name.slice(1);
console.log(cap_name+" serving on "+pro+"://"+require('os').hostname()+":"+options.port)
});
http._opa_servers[name] = {server : server, port : options.port}
if (!has_init_server) {
init_cookies(opts);
has_init_server = true;
}
return;
}
/**
* @register {string, Server.private.options, opa[option(string)], opa[option(string)], \
opa[option(string)], SSL.secure_type, \
continuation(WebInfo.private.native), \
(string, HttpRequest.msg_list, int -> bool) -> void}
*/
function init_server_cps(name, port, certfileo, privkeyo, passwdo, secure_type, dispatcher, ontransfer) {
function dispatcher_cps(winfo){
return_(dispatcher, winfo);
launch_schedule();
};
return init_server(name, port, certfileo, privkeyo, passwdo, secure_type, dispatcher_cps, ontransfer);
}
/**
* @register {-> opa[option(tuple_3(string, int, string))]}
*/
function get_remote_logs_params() {
console.warn("Warning: BslNet.Http_server.get_remote_logs_params is not yet implemented");
return js_none;
}
/**
* @register {WebInfo.private.native_request, \
opa[continuation(option(HttpRequest.multipart))] -> void}
* @cpsBypass
*/
function get_multipart_cps(req, cont) {
// TODO
// - Build file progressively
var form = new formidable.IncomingForm();
// formidable doesn't give access to raw headers, so we have to collect them
// on our own. As the Opa stdlib expects every part to have a name, we index
// parts by their names. Hence, the multipart object is a map from field names
// (strings) to {headers, value} / {headers, file}.
var data = {};
form.onPart = function (part) {
part.on('end', function () {
if (!(part.name in data)) {
// Ignore for now duplicate names
data[part.name] = {headers: part.headers};
}
});
form.handlePart(part);
};
form.on('field', function (name, value) {
data[name].value = value;
});
form.on('file', function (name, file) {
data[name].file = file;
});
form.on('error', function (err) {
// TODO: terminate HTTP request too?
form.emit('return', js_none);
});
form.on('end', function () {
form.emit('return', js_some(data));
});
form.once('return', function (result) {
return_(cont, result);
});
form.parse(req.request);
return;
}
// Stub
/**
* @register {WebInfo.private.native_request -> opa[option(HttpRequest.multipart)]}
*/
function get_multipart(req) {
assert(false);
}
// Fold function f across obj's fields and values, using
// acc as the starting value. f must be in cps, with arguments
// (field_name, field_value, acc, continuation(acc)). k is the
// continuation.
function fold_cps(obj, f, acc, k) {
var obj_fields = [];
for (var field in obj) obj_fields.push(field);
function iter(i) {
return function (acc) {
if (i == obj_fields.length) {
return return_(k, acc);
} else {
return f(obj_fields[i], obj[obj_fields[i]],
acc, cont(iter(i + 1)));
}
}
}
return iter(0)(acc);
}
/**
* @register {HttpRequest.multipart, 'acc, \
(HttpRequest.part, \
('a, (string, string, 'a, continuation('a) -> void), \
continuation('a) -> void), \
'acc, continuation('acc) -> void), \
continuation('acc) -> void}
* @cpsBypass
*/
function fold_multipart_cps(multipart, acc, folder, k) {
// Due to a bug in higher order projections, we have to bypass
// the cps transformation and manipulate continuations by hand.
function iter_multipart(name, part, acc, k) {
var res = empty_constructor();
add_field(res, 'name', name);
if ('file' in part) {
add_field(res, 'filename', part.file.filename);
add_field(res, 'content', function (cont) {
var res = empty_constructor();
add_field(res, 'content', binary_of_buffer(fs.readFileSync(part.file.path)));
return_(cont, res);
});
} else if ('value' in part) {
add_field(res, 'value', part.value);
}
function fold_header(acc, folder_h, k) {
// We need to convert header names to the format expected
// by Opa, i.e. "Header_Name"
var headers = {};
for (var name in part.headers) {
var newName =
name.replace("-", "_")
.replace(/(^|_)\w/g,
function (s) { return s.toUpperCase(); });
headers[newName] = part.headers[name];
}
return fold_cps(headers, folder_h, acc, k);
}
return folder(res, fold_header, acc, k);
}
return fold_cps(multipart, iter_multipart, acc, k);
}
/**
* @register {HttpRequest.multipart, 'acc, \
(HttpRequest.part, ('a, (string, string, 'a -> 'a) -> 'a), \
'acc -> 'acc) -> 'acc}
*/
function fold_multipart(multipart, acc, folder) {
assert(false);
}
/** @endModule */
/** @externType WebClient.failure */
/** @module Http_client */
/**
* @register {string, int, string, string, opa[option(string)], \
bool, opa[option(string)], opa[option(SSL.private_key)], \
opa[option(SSL.policy)], opa[option(time_t)], opa[option(string)], \
opa[list(string)], \
(string, int, string, opa[list(string)], \
(string, continuation(opa[option(string)]) -> void), \
continuation(opa[void]) -> void), \
continuation(WebClient.failure), continuation(opa[void]) -> void}
* @cpsBypass
*/
function place_request(hostname, port, path, request_kind, data, is_secure,
auth, private_key, policy, timeout, custom_agent,
more_headers, cont_success, cont_failure, cont_void) {
// TODO: Use CAs and policy with HTTPS.
var req;
var data = option2js(data);
var timeout = option2js(timeout);
var headers = {};
var custom_agent = option2js(custom_agent);
if (custom_agent) headers['user-agent'] = custom_agent;
var auth = option2js(auth);
// What we expect here actually is the raw authorization header. Therefore,
// we cannot use the auth option of request(), since it gets encoded.
if (auth) headers['authorization'] = auth;
list2js(more_headers).forEach(function (line) {
// Convert headers to format expected by request()
var fields = line.split(/: /);
headers[fields[0]] = fields[1];
});
var options = {
host: hostname,
port: port,
path: path,
method: request_kind,
headers: headers
};
function on_success(res) {
// HTTP data in node doesn't come at once, so we must build
// the response string progressively.
var data = "";
res.on("data", function (chunk) {
data += chunk;
});
var mime = res.headers["content-type"] || "text/plain";
var headers = [];
for (h in res.headers) {
headers.push(h + ": " + res.headers[h]);
}
var header_get = function (h, k) {
return_(k, js2option(res.headers[h]));
};
res.on("end", function () {
req.emit("return", "success", {
mime_type: mime,
code: res.statusCode,
content: data,
headers: js2list(headers),
header_get: header_get
});
});
}
function on_error(e) {
var res = empty_constructor();
if (e.syscall == "getaddrinfo") {
add_field(res, "network", js_void);
} else {
add_field(res, "other", toString(e));
}
req.emit("return", "failure", res);
}
function on_timeout() {
req.connection.end();
var res = empty_constructor();
add_field(res, "timeout", js_void);
req.emit("return", "failure", res);
}
function on_return(status, res) {
if (status == "success") {
// It is a shame that we need to unpack this value, since it'll be wrapped
// again by the continuation. We should fix that.
var c = cont(function (x) { return; });
cont_success(res.mime_type, res.code, res.content, res.headers, res.header_get, c);
} else {
return_(cont_failure, res);
}
}
if (is_secure) {
var private_key = option2js(private_key);
var policy = option2js(policy);
if (private_key) {
options.key = fs.readFileSync(private_key.cert_key);
options.passphrase = private_key.cert_pass;
options.cert = fs.readFileSync(private_key.cert_file);
if (private_key.ca_file || private_key.ca_path) {
error("Don't know how to deal with CAs in private key.");
}
}
if (policy) {
error("Don't know how to use a SSL policy.");
}
options.agent = false;
try {
req = https.request(options, on_success);
} catch (e) {
console.log(e);
var res = empty_constructor();
add_field(res, "ssl", js_void);
on_return("failure", res);
return_(cont_void, js_void);
return;
}
} else {
req = http.request(options, on_success);
}
if (data) req.write(data);
req.setTimeout(timeout, on_timeout);
req.on("error", on_error);
req.once("return", on_return);
req.end();
return_(cont_void, js_void);
return;
}
/** @endModule */
/** @module Requestdef */
// 1xx
/** @register {web_server_status} sc_Continue "SC_Continue" */
/** @register {web_server_status} sc_SwitchingProtocols "SC_SwitchingProtocols" */
// 2xx
/** @register {web_server_status} sc_OK "SC_OK" */
/** @register {web_server_status} sc_Created "SC_Created" */
/** @register {web_server_status} sc_Accepted "SC_Accepted" */
/** @register {web_server_status} sc_Non_AuthoritativeInformation "SC_Non_AuthoritativeInformation" */
/** @register {web_server_status} sc_NoContent "SC_NoContent" */
/** @register {web_server_status} sc_ResetContent "SC_ResetContent" */
/** @register {web_server_status} sc_PartialContent "SC_PartialContent" */
// 3xx
/** @register {web_server_status} sc_MultipleChoices "SC_MultipleChoices" */
/** @register {web_server_status} sc_MovedPermanently "SC_MovedPermanently" */
/** @register {web_server_status} sc_Found "SC_Found" */
/** @register {web_server_status} sc_SeeOther "SC_SeeOther" */
/** @register {web_server_status} sc_NotModified "SC_NotModified" */
/** @register {web_server_status} sc_UseProxy "SC_UseProxy" */
/** @register {web_server_status} sc_TemporaryRedirect "SC_TemporaryRedirect" */
// 4xx
/** @register {web_server_status} sc_BadRequest "SC_BadRequest" */
/** @register {web_server_status} sc_Unauthorized "SC_Unauthorized" */
/** @register {web_server_status} sc_PaymentRequired "SC_PaymentRequired" */
/** @register {web_server_status} sc_Forbidden "SC_Forbidden" */
/** @register {web_server_status} sc_NotFound "SC_NotFound" */
/** @register {web_server_status} sc_MethodNotAllowed "SC_MethodNotAllowed" */
/** @register {web_server_status} sc_NotAcceptable "SC_NotAcceptable" */
/** @register {web_server_status} sc_ProxyAuthenticationRequired "SC_ProxyAuthenticationRequired" */
/** @register {web_server_status} sc_RequestTime_out "SC_RequestTime_out" */
/** @register {web_server_status} sc_Conflict "SC_Conflict" */
/** @register {web_server_status} sc_Gone "SC_Gone" */
/** @register {web_server_status} sc_LengthRequired "SC_LengthRequired" */
/** @register {web_server_status} sc_PreconditionFailed "SC_PreconditionFailed" */
/** @register {web_server_status} sc_RequestEntityTooLarge "SC_RequestEntityTooLarge" */
/** @register {web_server_status} sc_Request_URITooLarge "SC_Request_URITooLarge" */
/** @register {web_server_status} sc_UnsupportedMediaType "SC_UnsupportedMediaType" */
/** @register {web_server_status} sc_RequestedRangeNotSatisfiable "SC_RequestedRangeNotSatisfiable" */
/** @register {web_server_status} sc_ExpectationFailed "SC_ExpectationFailed" */
// 5xx
/** @register {web_server_status} sc_InternalServerError "SC_InternalServerError" */
/** @register {web_server_status} sc_NotImplemented "SC_NotImplemented" */
/** @register {web_server_status} sc_BadGateway "SC_BadGateway" */
/** @register {web_server_status} sc_ServiceUnavailable "SC_ServiceUnavailable" */
/** @register {web_server_status} sc_GatewayTime_out "SC_GatewayTime_out" */
/** @register {web_server_status} sc_HTTPVersionNotSupported "SC_HTTPVersionNotSupported" */
var status_code_map = {
'SC_Continue':100,
'SC_SwitchingProtocols':101,
'SC_OK':200,
'SC_Created':201,
'SC_Accepted':202,
'SC_Non_AuthoritativeInformation':203,
'SC_NoContent':204,
'SC_ResetContent':205,
'SC_PartialContent':206,
'SC_MultipleChoices':300,
'SC_MovedPermanently':301,
'SC_Found':302,
'SC_SeeOther':303,
'SC_NotModified':304,
'SC_UseProxy':305,
'SC_TemporaryRedirect':307,
'SC_BadRequest':400,
'SC_Unauthorized':401,
'SC_PaymentRequired':402,
'SC_Forbidden _':403,
'SC_NotFound':404,
'SC_MethodNotAllowed':405,
'SC_NotAcceptable':406,
'SC_ProxyAuthenticationRequired':407,
'SC_RequestTime_out':408,
'SC_Conflict':409,
'SC_Gone':410,
'SC_LengthRequired':411,
'SC_PreconditionFailed':412,
'SC_RequestEntityTooLarge':413,
'SC_Request_URITooLarge':414,
'SC_UnsupportedMediaType':415,
'SC_RequestedRangeNotSatisfiable':416,
'SC_ExpectationFailed':417,
'SC_InternalServerError':500,
'SC_NotImplemented':501,
'SC_BadGateway':502,
'SC_ServiceUnavailable':503,
'SC_GatewayTime_out':504,
'SC_HTTPVersionNotSupported':505
};
/**
* @register {web_server_status -> int}
*/
function status_code(code) {
return (code in status_code_map) ? status_code_map[code] : -1;
}
var reason_phrase_map = {
100:"Continue",
101:"Switching Protocols",
200:"OK",
201:"Created",
202:"Accepted",
203:"Non-Authoritative Information",
204:"No Content",
205:"Reset Content",
206:"Partial Content",
300:"Multiple Choices",
301:"Moved Permanently",
302:"Found",
303:"See Other",
304:"Not Modified",
305:"Use Proxy",
307:"Temporary Redirect",
400:"Bad Request",
401:"Unauthorized",
402:"Payment Required",
403:"Forbidden",
404:"Not Found",
405:"Method Not Allowed",
406:"Not Acceptable ",
407:"Proxy Authentication Required",
408:"Request Time-out",
409:"Conflict",
410:"Gone",
411:"Length Required",
412:"Precondition Failed",
413:"Request Entity Too Large",
414:"Request-URI Too Large",
415:"Unsupported Media Type",
416:"Requested range not satisfiable",
417:"Expectation Failed",
500:"Internal Server Error",
501:"Not Implemented",
502:"Bad Gateway",
503:"Service Unavailable",
504:"Gateway Time-out",
505:"HTTP Version not supported"
};
/**
* @register {int -> string}
*/
function reason_phrase(code) {
return (code in reason_phrase_map) ? reason_phrase_map[code] : "reason_phrase";
}
/**
* @register {WebInfo.private.native_request, continuation(string) -> void}
* @cpsBypass
*/
function get_request_message_body(req, k) {
if (typeof req._body_barrier == 'undefined') {
var body = new Array();
var barrier = new Barrier("get_request_message_body")
req._body_barrier = barrier;
req.request.on('data', function(chunk){
body.push(chunk);
}
);
req.request.on('end', function(){
barrier.release(body.join(""));
}
);
}
req._body_barrier.wait(k);
return;
}
/**
* @register {WebInfo.private.native_request, string, string -> \
WebInfo.private.native_request}
*/
function request_with(req, url, body) {
req._body_barrier = new Barrier("request_with");
req.request.url = url;
req._body_barrier.release(body);
return req;
}
// Note : Really hacky in node backend the response are sended by make_response*
// and the web info continuation do nothing.
// We need to fix it, probably by changing the bsl interface.
/**
* @register {WebInfo.private.native_request, string, string, \
(string, continuation(opa[void]) -> void), \
continuation(opa['a]) -> void}
* @cpsBypass
*/
function request_with_cont(req, url, body, f, k) {
var r = {request : req.request,
cookies : req.cookies,
response : new DummyOpaResponse(function(s){f(s, ccont(k, function(){}))})
};
r = request_with(r, url, body);
return_(k, {f1 : r, f2 : function(_r, _k){}});
return;
}
/**
* @register {WebInfo.private.native_request -> string}
*/
function get_cookie(req) {
var s = req.cookies.ic
return s != null ? s : "";
}
/**
* @register {WebInfo.private.native_request -> opa[option(string)]}
*/
function get_request_cookie(req) {
var r = get_cookie(req);
return r == "" ? js_none : js_some(r);
}
/**
* @register {WebInfo.private.native_request -> string}
*/
function get_request_ua(req) {
return get_user_agent(req);
}
/** @endModule */
Jump to Line
Something went wrong with that request. Please try again.