From 5df79c53745fde5d6c3340a2979b1429cd5892c1 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 8 Oct 2011 20:30:28 +0200 Subject: Initial import of htcd system 1.0 Signed-off-by: Henrik Rydberg --- src/net/htcd.cc | 153 +++++++++++++++++++++++++++++++ src/net/isocket.cc | 58 ++++++++++++ src/net/isocket.h | 68 ++++++++++++++ src/net/redirect.cc | 185 ++++++++++++++++++++++++++++++++++++++ src/net/route80.cc | 64 +++++++++++++ src/net/server.cc | 75 ++++++++++++++++ src/net/server.h | 46 ++++++++++ src/net/test.cc | 44 +++++++++ src/net/thrserv.cc | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/net/thrserv.h | 79 ++++++++++++++++ 10 files changed, 1027 insertions(+) create mode 100644 src/net/htcd.cc create mode 100644 src/net/isocket.cc create mode 100644 src/net/isocket.h create mode 100644 src/net/redirect.cc create mode 100644 src/net/route80.cc create mode 100644 src/net/server.cc create mode 100644 src/net/server.h create mode 100644 src/net/test.cc create mode 100644 src/net/thrserv.cc create mode 100644 src/net/thrserv.h (limited to 'src/net') diff --git a/src/net/htcd.cc b/src/net/htcd.cc new file mode 100644 index 0000000..89729e6 --- /dev/null +++ b/src/net/htcd.cc @@ -0,0 +1,153 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +static fp_stream merr(stderr); + +static mstring PATH; +static htcServ* SERV; + +const mstring PIDURI="/sys/htcd"; + +const int BACKLOG=4; +const int SERVERS=9; +const int LOWER=3; +const int UPPER=6; +const int KEEPALIVE=300; +const int SNDBUFSIZE=16384; +const int RCVBUFSIZE=16384; + +/////////////////////////////////////////////////////////// + +static thrServ* server[SERVERS]; +static thrCron* crontab; + +extern "C" { + static void* thr_handler(void* p) { ((thrServ*)p)->enter(); return 0; } + static void* thr_cron(void* p) { ((thrCron*)p)->enter(); return 0; } + static void onTERM(int sig) { SERV->htcd.running=0; } + static void onHUP(int sig) { if(crontab) crontab->running=0; } + typedef void (*disp_t)(int); +} + +static void MainProc(isocket& sock) +{ + int keep=1; + while(SERV->htcd.running) { + int nvacant=0; for(int i=0;ivacant()) nvacant++; + if(nvacant>UPPER) keep=1; else if(nvacant<=LOWER) keep=0; + if(!keep) for(int i=0;ishutdown(); + time_t timeout=time(0)-KEEPALIVE; + for(int i=0;iserver[i]->last) server[i]->disconnect(); + int test=sock.test(); + if(test>0) { + thrServ* p=0; + for(int i=0;ivacant()) { p=server[i]; break; } + if(!p) for(int i=0;ifirstfirst) p=server[i]; + if(p->shutdown()) { + try { + isocket* s=sock.accept(); + if(s) p->reset(s,keep); + } + catch(const merror_t& e) { + merr<<"SOCKET: "<htcd.running=0; + } + for(int i=0;iterm(); + pthread_join(server[i]->ptid,0); + } + pthread_kill(crontab->tid,SIGHUP); + pthread_join(crontab->ptid,0); +} + +/////////////////////////////////////////////////////////////// + +static void httpServer(int overport) +{ + isocket sock(SERV->htcd.node.addr,overport,BACKLOG); + sock.keepalive(1); + sock.sendbufsize(SNDBUFSIZE); + sock.recvbufsize(RCVBUFSIZE); + + sigignore(SIGPIPE); + disp_t onterm=signal(SIGTERM,onTERM); + disp_t onint=signal(SIGINT,onTERM); + disp_t onhup=signal(SIGHUP,onHUP); + MainProc(sock); + signal(SIGHUP,onhup); + signal(SIGINT,onint); + signal(SIGTERM,onterm); +} + +static void Daemon(int overport) try +{ + PROCLOCK(PATH+PIDURI); + SERV=new htcServ(PATH); + crontab=new thrCron(*SERV); + pthread_create(&crontab->ptid,0,thr_cron,crontab); + for(int i=0;iptid,0,thr_handler,server[i]); + } + merr<<"htcd: "<htcd.port:overport); + merr<<"htcd: "<htcd.errlog<<"["<htcd.errlog<<"["< [-1|0|1]\n"; + return -1; + } + PATH=argv[1]; if(PATH.back()=='/') PATH=PATH.popb(); + setenv("DOCROOT",PATH.c_str(),1); + int overport=atoi(argv[2]); + int cmd=argc>3?atoi(argv[3]):0; + if(cmd) ProcTerminate(PATH+PIDURI); + if(cmd>=0) { if(fork()==0) Daemon(overport); } + delete SERV; + return 0; +} +catch(const merror_t& e) { + merr<<"ERROR: ["< +#include + +isocket::isocket(const in_addr& ia,int port,int backlog): + pipe_stream(-1),rd(-1) +{ + if(port) { + fd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if(fd<0) THROW("isocket: Could not create"); + reuse(1); + } + else fd=0; + sa.sin_family=2; + sa.sin_port=htons(port); + sa.sin_addr=ia; + if(port) { + if(bind(fd,(sockaddr*)&sa,sizeof(sa))==-1) THROW("isocket: Could not bind"); + } + if(listen(fd,backlog)==-1) THROW("isocket: Could not listen"); + rd.setfd(fd); +} + +isocket::isocket(int f,const sockaddr_in& ia): + pipe_stream(f),rd(f) +{ + sa=ia; + setflag(O_NONBLOCK|O_NDELAY); + nodelay(1); +} + +isocket* isocket::accept() throw(merror_t) +{ + sockaddr_in tsa; socklen_t tsalen; + memset(&tsa,0,tsalen=sizeof(tsa)); + int tfd=::accept(fd,(sockaddr*)&tsa,&tsalen); + if(tfd>0) return new isocket(tfd,tsa); + if(errno!=EAGAIN) THROW("isocket: could not accept"); + return 0; +} diff --git a/src/net/isocket.h b/src/net/isocket.h new file mode 100644 index 0000000..5ca1ec3 --- /dev/null +++ b/src/net/isocket.h @@ -0,0 +1,68 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ISOCKETH +#define ISOCKETH + +#include +#include +#include +#include +#include +#include + +class isocket : public pipe_stream { +public: + isocket(const in_addr& ia,int port,int backlog); + isocket(int f,const sockaddr_in& ia); + ~isocket() { close(); } + + void close() { if(fd>=0) { ::close(fd); fd=-1; }} + + int getfd() const { return fd; } + int family() const { return sa.sin_family; } + int port() const { return ntohs(sa.sin_port); } + in_addr address() const { return sa.sin_addr; } + + void reuse(int ok) { set(SOL_SOCKET,SO_REUSEADDR,ok); } + void nodelay(int ok) { set(IPPROTO_TCP,TCP_NODELAY,ok); } + void keepalive(int ok) { set(SOL_SOCKET,SO_KEEPALIVE,ok); } + void sendbufsize(int s) { set(SOL_SOCKET,SO_SNDBUF,s); } + void recvbufsize(int s) { set(SOL_SOCKET,SO_RCVBUF,s); } + + isocket* accept() throw(merror_t); + + int test() { return rd.poll(); } + int getsome(char* s,int n) { return rd.read(s,n); } + int gethead(char* s,int n) { return rd.readpara(s,n); } + int getbody(char* s,int n) { return rd.readx(s,n); } + +protected: + void setflag(int f) { fcntl(fd,F_SETFL,f); } + int getflag() const { return fcntl(fd,F_GETFL,0); } + void set(int lev,int opt,int ok) throw(merror_t) { + if(setsockopt(fd,lev,opt,(char*)&ok,sizeof(ok))<0) + THROW("isocket: Could not set option"); + } +private: + sockaddr_in sa; + mread rd; +}; + +#endif diff --git a/src/net/redirect.cc b/src/net/redirect.cc new file mode 100644 index 0000000..7e07e7c --- /dev/null +++ b/src/net/redirect.cc @@ -0,0 +1,185 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +static fp_stream merr(stderr); +static IPnode NODE; +static int PORT; +static mstring URL,PATH,SERVER,URL1,URL2; +static int volatile RUNNING; + +const mstring REDIRECT="/tmp/redirect."; +const int BACKLOG=4; +const int SNDBUFSIZE=16384; +const int RCVBUFSIZE=16384; +const int MAXHEAD=16384; +const int SAFEZONE=4096; + +static void BreakURL() +{ + static const mstring pre="//"; + int p=URL.find(pre); + if(p>=0) { + p+=pre.size(); + int q=p+URL.right(p).find('/'); + if(q>=p) { + URL1=URL.left(q); + URL2=URL.right(q); + } + else { + URL1=URL; + URL2.clear(); + } + } + else throw(0); +} + +static mstring Combine(const sref& uri) +{ + const int ncvt=3; + static const mstring cvt[ncvt]={"/BODY","/en","/sv"}; + mstring d=uri; + mstring s=URL1; + for(int i=0;i=0) { + d.erase(p,cvt[i].size()); + s+=cvt[i]; + } + } + s+=URL2; + s+=d; + return s; +} + +static int Handle(isocket& sock) +{ + mstring buffer(MAXHEAD+SAFEZONE); + int nhead=sock.gethead(buffer.data(),MAXHEAD); + if(nhead<=0) return 0; + httpReq req(sref(buffer.data(),nhead)); + httpResp resp; + resp.status=HTTP_301; + //resp.location=URL+req.uri; + resp.location=Combine(req.uri); + if(req.arg.nempty()) { resp.location.append(1,'?'); resp.location+=req.arg; } + resp.putHead(sock); + putend(sock); + sock.close(); + return 1; +} + +/////////////////////////////////////////////////////////////// + +extern "C" { + static void onTERM(int sig) { RUNNING=0; } + typedef void (*disp_t)(int); +} + +static void MainProc(isocket& sock) +{ + RUNNING=1; + while(RUNNING) { + int test=sock.test(); + if(test>0) { + try { + isocket* s=sock.accept(); + if(s) Handle(*s); + } + catch(const merror_t& e) { + merr<<"SOCKET: "< "< [-1|0|1]\n"; + return -1; + } + sref s(argv[1]); + mstring host=s.left_last(':'); + mstring sport=s.past_last(':'); + if(sport.empty()) { + sport=host; + host.clear(); + } + PORT=atoi(sport); + URL=argv[2]; + if(URL.back()=='/') URL=URL.popb(); + BreakURL(); + if(host.empty()) { + struct utsname u; uname(&u); + host=u.nodename; + } + Lookup(NODE,host); + SERVER=NODE.name; SERVER.append(1,'-'); SERVER+=itoa(PORT); + PATH=REDIRECT; PATH+=SERVER; + int cmd=argc>3?atoi(argv[3]):0; + if(cmd) ProcTerminate(PATH); + if(cmd>=0) { if(fork()==0) Daemon(); } + return 0; +} +catch(const merror_t& e) { + merr<<"ERROR: ["< +#include +#include + +static fp_stream merr(stderr); +static IPnode NODE; +static mstring PATH; +static int PORT; + +const int BACKLOG=4; +const int SNDBUFSIZE=16384; +const int RCVBUFSIZE=16384; + +/////////////////////////////////////////////////////////////// + +static void MainProc(isocket& sock) +{ + int fd=dup2(sock.fd,0); + if(fd!=0) { merr<<"Not right\n"; return; } + execl("htcd","htcd",PATH.c_str(),"-1",0); +} + +/////////////////////////////////////////////////////////////// + +static void httpServer() +{ + isocket sock(NODE.addr,PORT,BACKLOG); + sock.keepalive(1); + sock.sendbufsize(SNDBUFSIZE); + sock.recvbufsize(RCVBUFSIZE); + MainProc(sock); +} + +main(int argc,char* argv[]) +{ + if(argc<3) { merr<<"Usage: "< \n"; return -1; } + PATH=argv[1]; if(PATH.back()=='/') PATH=PATH.popb(); + PORT=atoi(argv[2]); + struct utsname u; uname(&u); + Lookup(NODE,sref(u.nodename)); + httpServer(); + return 0; +} + + diff --git a/src/net/server.cc b/src/net/server.cc new file mode 100644 index 0000000..6996b20 --- /dev/null +++ b/src/net/server.cc @@ -0,0 +1,75 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +const mstring SERVURI="/htc/server.htc"; + +htcServ::htcServ(const sref& path) : htcd(path),user(htcd.toks) +{ + Setup(user,htcd); + LoadBuffer(server,path+SERVURI); +} + +/////////////////////////////////////////////////////// + +static void Log(fp_stream& tlog,htcd_req& R,const sref& status) +{ + MLOCK(R.htcd()->mutex); + tlog<<"["<node.ip<<"][" + <node.name<<":"<port<<"][" + <method<<"][" + <uri<<"]["<arg<<"]\n"; + tlog.flush(); +} + +/////////////////////////////////////////////////////// + +int SendStatus(isocket& out,htcd_req& R,int keep,const sref& status,const sref& msg) +{ + R.resp.status=status; + if(atof(R.req->version.past_first(http_ver))method,HTTP_HEAD)) { + R.resp.cont_type.clear(); + R.resp.cont_length.clear(); + } + else { + if(R.resp.cont_type.empty()) R.resp.cont_type=http_mime_default; + R.resp.cont_length=itoa(msg.size()); + } + Log(R.htcd()->logger,R,status); + R.resp.putHead(out); + putend(out); + out< +#include +#include +#include +#include + +const mstring http_ver="HTTP/"; +const double http_minor=1.0,http_major=1.1; + +const mstring keep_tag="Keep-Alive"; +const mstring close_tag="Close"; +const mstring expect_continue_tag="100-continue"; + +struct htcServ { + HTCd htcd; + tokmap user; + mstring server; + htcServ(const sref& path); +}; + +int SendStatus(isocket& out,htcd_req& R,int keep, + const sref& status,const sref& msg=ms_empty); + +#endif diff --git a/src/net/test.cc b/src/net/test.cc new file mode 100644 index 0000000..c0e3b41 --- /dev/null +++ b/src/net/test.cc @@ -0,0 +1,44 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +using namespace std; + +static mutex_t mutex; +static mtcond_t cond; + +static void* thread(void* a) +{ + cerr<<"thread "< +#include + +const mstring cron_tag="\\run.cron"; +const mstring FATAL="FATAL ERROR"; +const mstring UNKNOWN="UNKNOWN"; +const mstring REJECTED="REJECTED"; + +const int MAXHEAD=16384; +const int MAXBODY=2<<21; +const int MAXURI=4096; +const int MAXARG=4096; +const int SAFEZONE=4096; + +/////////////////////////////////////////////////////////////// + +thrServ::thrServ(htcServ& server): + pmutex(MUTEX_INIT), + pcond(MTCOND_INIT), + keepalive(0),running(0),termok(0), + first(time(0)),last(time(0)), + serv(&server), + sock(0), + user(server.user,server.htcd.docroot) +{ +} + +thrServ::~thrServ() +{ + delete sock; +} + +void thrServ::reset(isocket* s,int keep) +{ + keepalive=keep; + running=1; + termok=0; + first=time(0); + last=time(0); + sock=s; + while(!tid); + if(MCONTINUE(pcond)) throw((int)0); +} + +void thrServ::Log(const sref& msg) +{ + MLOCK(serv->htcd.mutex); + if(sock) serv->htcd.errlog<<"["<address())<<":"<port()<<"][" + <htcd.errlog<<"["<htcd.errlog.flush(); +} + +void thrServ::enter() +{ + tid=pthread_self(); + MSUSPEND(pmutex,pcond) + while(serv->htcd.running&&!termok) { + session(); + delete sock; + sock=0; + if(serv->htcd.running&&!termok) { + MSUSPEND(pmutex,pcond) + } + } +} + +void thrServ::session() try +{ + int keep=1; + while(sock&&keep&&running) { + int test=sock->test(); + if(test>0) { last=time(0); keep=handle(); } + else if(test<0) keep=0; + } +} +catch(const merror_t& e) { + Log(e.desc); +} +catch(...) { + Log(FATAL); +} + +/////////////////////////////////////////////////////////////// + +thrCron::thrCron(htcServ& server): + tid(0),running(0),serv(&server),user(server.user,server.htcd.docroot) +{ +} + +void thrCron::Log(const sref& msg) +{ + MLOCK(serv->htcd.mutex); + serv->htcd.errlog<<"["<htcd.errlog.flush(); +} + +void thrCron::enter() try +{ + tid=pthread_self(); + tcpReq tcp; tcp.port=0; + httpReq req; + mstream devnull; + htcd_req R(serv->htcd,user.toks,devnull,tcp,req); + Env env(serv->htcd.ops,R,XSYSTEM); + env.req->running=&running; + running=1; + mstream m; + env.parse(m,cron_tag); +} +catch(const merror_t& e) { + Log(e.desc); +} +catch(...) { + Log(FATAL); +} + + +/////////////////////////////////////////////////////////////// + +int thrServ::handle() +{ + tcpReq tcp; + tcp.port=sock->port(); + if(!Lookup(tcp.node,sock->address())) { + Log(REJECTED); + return 0; + } + + ////////////////////////////////////////////////////////////////// + + mstring buffer(MAXHEAD+MAXBODY+SAFEZONE); + int nhead=sock->gethead(buffer.data(),MAXHEAD); + if(nhead==0) return 0; + else if(nhead<0) return 0; + + sref message(buffer.data(),nhead); + +#if 0 + static fp_stream merr(stderr); + merr<<"QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\n"; + merr<htcd,user.toks,cache,tcp,req); + + ////////////////////////////////////////////////////////////////// + + if(nhead>=MAXHEAD) return SendStatus(*sock,R,keep,HTTP_413); + + if(eqn(req.method,HTTP_TRACE)) { + R.resp.cont_type="message/http"; + return SendStatus(*sock,R,keep,HTTP_200,buffer.left(nhead)); + } + + if(req.expect.nempty()) { + if(eqn(req.expect,expect_continue_tag)) SendStatus(*sock,R,keep,HTTP_100); + else return SendStatus(*sock,R,keep,HTTP_417); + } + + if(req.trans_enc.nempty()||req.cont_enc.nempty()) return SendStatus(*sock,R,0,HTTP_501); + + ////////////////////////////////////////////////////////////////// + + int ilength=atoi(req.cont_length); + if(ilength>MAXBODY) return SendStatus(*sock,R,0,HTTP_413); + else if(ilength>0) { + int nbody=sock->getbody(buffer.data()+nhead,ilength); + if(nbody<0) return 0; + else { + sref sbody(buffer.data()+nhead,nbody); +#if 0 + static fp_stream merr(stderr); + merr<<"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP\n"; + merr<htcd.ops,R,XSYSTEM); + env.req->running=&running; + mstream m; + env.parse(m,serv->server); + if(!env.req->is_running()) THROW("htc: "< +#include +#include + +//////////////////////////////////////////////////////// +// +// MT - safe +// Note that mutexes aren't needed here, since control +// is flowing from main to child, with possible child +// override in local variables. + +struct thrServ { + pthread_t ptid; + mutex_t pmutex; + mtcond_t pcond; + volatile int tid,keepalive,running,termok; + time_t first,last; + htcServ* serv; + isocket* sock; + htcd_user user; + + thrServ(htcServ& server); + ~thrServ(); + + void Log(const sref& msg); + void reset(isocket* s,int keep); + + int vacant() const { return sock==0; } + int shutdown() { keepalive=0; return sock==0; } + void disconnect() { keepalive=0; running=0; } + void term() { + keepalive=0; + running=0; + termok=1; + MCONTINUE(pcond); + } + + void enter(); + void session(); + int handle(); +}; + +//////////////////////////////////////////////////////// + +struct thrCron { + pthread_t ptid; + volatile int tid,running; + htcServ* serv; + htcd_user user; + + thrCron(htcServ& server); + + void Log(const sref& msg); + void enter(); +}; + +#endif -- cgit v1.2.3