code |
import scala.concurrent.Process;
import scala.concurrent.Process._;
import scala.concurrent.NameServer;
import java.net.Socket;
class Request {};
case class Header(buf:List[char]) extends Request {};
case class HeaderDone(buf:List[char],after:Binary) extends Request {};
case class Post(buf:List[char],len:int,info:Tuple5[Any,Any,Any,Any,Any]) extends Request {};
class HttpDriver {
type handler = Process => unit;
def start(port:int, fun:handler, max:int) =
spawn({server(port, fun, max);()});
def server(port:int, fun:handler, max:int) =
new TcpServer(port,
(socket:Process) => input_handler(socket, fun),
max,
0);
private def input_handler(socket:Process, fun:handler) = {
val s = self;
val server = spawn_link({fun(s);});
relay(socket, server, Header(Nil));
}
private def relay(Socket:Process,
Server:Process,
state:Request):unit = {
receive {
case Tuple3('tcp, Socket, bin:Binary) => {
parse_request(state, socket, server, bin);
}
// case Tuple3('tcp_closed, Socket) => {
// server ! Pair(self, 'closed);
// }
case Pair(Server, 'close) => {
Socket ! Pair(self,close);
}
case Pair(Server, Pair(headers:String, data:Binary)) => {
val headers1 = headers + "Content-Length " +
data.length + "\r\n\r\n";
Socket.getOutputStream().write(headers1);
Socket.getOutputStream().write(data);
relay(Socket, server, state);
}
case Tuple3('EXIT, Server, _) => {
Socket!Pair(self,close);
}
}
}
def parse_request(request:Request,socket:Process,
server:Process,data:Binary) = {
request.match {
case Header(buff) => {
scan_header(data, buff).match {
case Header(buff1) =>
relay(socket, server, Header(buff1));
case HeaderDone(header, after) =>
got_header(socket, server, header, after)
}
}
case Post(buff, len, x) => {
collect_chunk(len, data, buff).match {
case Tuple3('yes, postdata, after) => {
val args2 = parse_uri_args(postdata);
val Tuple5(op,vsn,uri,args1,env) = x;
val req = Tuple5(op,vsn,uri,args1+args2,env);
server ! Pair(self, req);
parse_request(Header(Nil), socket, server, after);
}
case Tuple3('no,buff1, len1) => {
val state = Post(buff1, len1, x);
relay(socket, server, state);
}
}
}
}
}
def got_header(socket:Process, server:Process,
header:List[char], after:Binary) = {
val result = parse_header(header);
result.match {
case Tuple6(op, contentLen, vsn, uri, args, env) => {
if (ContentLen == 0) {
server ! Pair(self, Tuple5(op,vsn,uri,args,env));
parse_request(Header(Nil), socket, server, after);
} else {
val state = Post(Nil, contentLen, Tuple5(op,vsn,uri,args,env));
parse_request(state, socket, server, after);
}
}
}
}
def collect_chunk(n:int,newl:List[A],buf:List[A]) = {
if (n == 0) Tuple3('yes, reverse(buf), newl);
else newl.match {
case h::t => {collect_chunk(n-1,t,h::buff);}
case Nil => Tuple3('no, buff, n)
}
}
def scan_header(In:Binary,Out:List[char]):Request = {
if (In.length == 0) Header(Out) else {
val t = In.drop(1);
Out.match {
case List('\n','\r','\n','\r',l@(_*)) => HeaderDone(l.reverse,t);
case _ => scan_header(t,In(0)::Out);
}
}
}
//----------------------------------------------------------------------
class classifier with java.net.FileNameMap {
def classify(filename:String) =
getContentTypeFor(filename);
}
def header(ctype:String) = {
ctype.match {
case "text" =>
"HTTP/1.0 200 Ok\r\n" + powered_by + content_type("text/html");
case "html" =>
"HTTP/1.0 200 Ok\r\n" + powered_by + content_type("text/html");
case "jpg" =>
"HTTP/1.0 200 Ok\r\n" + powered_by + content_type("image/jpeg");
case "gif" =>
"HTTP/1.0 200 Ok\r\n" + powered_by + content_type("image/gif");
// case "redirect,To}) ->
// ["HTTP/1.0 302 Come and get it!\r\n",
// powered_by(), "Location: " ++ To ++ "\r\n"].
}
}
def powered_by = "X-Powered-By: Scala \r\n";
def content_type(x:String) = "Content-Type: " + x + "\r\n";
//----------------------------------------------------------------------
def parse_header(str:List[char]) = {
val hd::tail = split(str);
val prequest = parse_request(hd);
val pargs = tail.map(x => isolate_arg(x));
make_return_value(prequest, pargs);
}
def split(s:List[char]):List[List[char]] = {
s.match {
case List(x@(_*),'\r','\n',y@(_*)) => x::split(y);
case Nil => Nil;
}
}
def make_return_value(rval:Tuple3[List[char],List[char],Pair[List[char],List[char]]],env:List[Tuple2[List[char],List[char]]]) = {
val Tuple3(op,vsn,Pair(uri,args)) = rval;
Tuple6(op, content_length(env), vsn, uri, args, env);
}
def content_length(valuePairs:List[Pair[List[char],List[char]]]):int = {
valuePairs.match {
case Pair(str,value)::t =>
str.match {
case List('c','o','n','t','e','n','t','-','l','e','n','g','t','h') => value.toInteger;
case _ => content_length(t);
}
case Nil => 0;
}
}
def urlencoded2str(str:List[char]):List[char] = {
str.match {
case List('%',hi,lo,t@(_*)) =>
decode_hex(hi, lo)::urlencoded2str(t);
case List('+',t@(_*)) => ' '::urlencoded2str(t);
case h::t => h::urlencoded2str(t);
case Nil => Nil;
}
}
def isolate_arg(str:List[char]) = isolate_arg(str, Nil);
def isolate_arg(str:List[char], L:List[char]) = {
str.match {
case List(':',' ',t@(_*)) => Pair(to_lower(l.reverse),t);
case h::t => isolate_arg(t, h::l)
}
}
// decode_hex %%
def decode_hex(hex1:char, hex2:char) =
hex2dec(hex1)*16 + hex2dec(hex2);
def hex2dec(x:char):char = {
if (x >= '0' & x <= '9') (x.asInstanceOf[int] - '0'.asInstanceOf[int]).asInstanceOf[char];
else
x.match{
case 'A' => 10;
case 'B' => 11;
case 'C' => 12;
case 'D' => 13;
case 'E' => 14;
case 'F' => 15;
case 'a' => 10;
case 'b' => 11;
case 'c' => 12;
case 'd' => 13;
case 'e' => 14;
case 'f' => 15;
}
}
def parse_request(str:List[char]) = {
val Pair('ok, args) = split(Str);
args.match {
case "POST"::uri::vsn => Post(parse_vsn(vsn) ,parse_uri(uri));
case "GET"::uri::vsn => Get(parse_vsn(vsn), parse_uri(uri));
}
}
def parse_vsn(x:List[char]) = {
x.match{
case List('H','T','T','P','/',v,'.',m) => Pair(v-'0',m-'0');
case _ => Pair(0,0);
}
}
// A typical URI looks
// like
// URI = "/a/b/c?password=aaa&invisible=A+hidden+value"
def tokens(string:List[char],Chars:List[char]):List[List[char]] =
tokens(string,Chars,Nil);
def tokens(string:List[char],
Chars:List[char],
acc:List[char]):List[List[char]] = {
prefix(Chars,string).match {
case Some(tail) => acc.reverse :: tokens(tail,Chars,Nil);
case None => tokens(string.tail,Chars,string.head::acc);
}
}
def prefix(p:List[char],l:List[char]):Option[List[char]] = {
if (p == Nil) Some(l);
else
if(p.head == l.head)
prefix(p.tail,l.tail)
else
None
}
def parse_uri(uri:List[char]) = {
tokens(uri, '?'::Nil).match {
case root::Nil => Pair(root, Nil);
case root::args::Nil =>
Pair(root, parse_uri_args(args))
}
}
def parse_uri_args(args:List[char]) = {
val args1 = tokens(args, List('&',';'));
args1.map((keyval:List[char]) =>
tokens(keyval, '='::Nil).match {
case List(key,value) =>
Pair(urlencoded2str(key), urlencoded2str(value));
case List(key) =>
Pair(urlencoded2str(key), Nil);
}
)
}
} |