【智能路由器】系列文章连接
http://blog.csdn.net/u012819339/article/category/5803489
goahead是路由器上使用率较高的几个web服务器之一,官网地址:http://embedthis.com/goahead/
这里主要介绍怎么在goahead进行开发,网上有一份源码流程解释,比较清晰,这里不得不贴一下。
--------------------------------------------------------------------------------------
MAIN(goahead, int argc, char **argv, char **envp)
-------------------------------------初始化过程开始-------------------------------------
|-->for(argv[...]){} //解析命令行
|-->initPlatform(); //注册信号处理函数
|-->signal(SIGTERM, sigHandler) ...
|-->websOpen(documents, route) //初始化服务器
|-->websOsOpen(); //生成系统启动随机数(标识系统)
|-->websRuntimeOpen(); //开始系统计时sym、symMax
|-->websTimeOpen(); //初始化hash结构timeTokens
|-->logOpen(); //初始化基本日志
|-->setFileLimits(); //设置最大文件描述符值
|-->socketOpen(); //初始化WebsSocket **socketList=NULL,socketMax =0,socketHighestFd=-1
|-->setLocalHost(); //设置服务器的hostname(websHost), 服务器的IP地址(websIpAddr)
|-->sslOpen(); //若支持ssl则初始化ssl相关信息
|-->sessions = hashCreate(-1); //初始化话hash结构sessions
|-->websStartEvent(...pruneSessions...); //启动定时调度函数pruneSessions ,pruneSessions每秒调度清除过期的sessions
|-->websSetDocuments(documents); //设置goahead访问根目录websDocuments
|-->websOpenRoute(); //初始化hash结构handlers,相应函数加入到handlers中.
|-->websDefineHandler("continue", continueHandler, 0, 0, 0); //continue的处理函数为continueHandler
|-->websDefineHandler("redirect", redirectHandler, 0, 0, 0); //redirect处理函数为redirectHandler
|-->websCgiOpen(); //定义cgi处理函数加入到handlers中.
|-->websDefineHandler("cgi", 0, cgiHandler, 0, 0); //定义cgi的处理函数为cgiHandler
|-->websOptionsOpen(); //定义options处理函数加入到handlers中.
|-->websDefineHandler("options", 0, optionsHandler, 0, 0); //定义options的处理函数为optionsHandler
|-->websActionOpen(); //初始化hash结构actionTable,定义action处理函数加入到handlers中,定义action对应值的函数加入到actionTable中. .
|-->websDefineHandler("action", 0, actionHandler, closeAction, 0); //定义action的处理函数为actionHandler
|-->WebActionDefineInit(); //初始化action对应值的处理函数
|-->websDefineAction("upgrade", ActionDefault); //定义action=upgrade的处理函数为ActionDefault,
|-->websDefineAction("...", ...); //同上的类似函数
|-->websFileOpen(); //设置默认页面websIndex="index.html",定义action处理函数加入到handlers中
|-->websDefineHandler("file", 0, fileHandler, fileClose, 0); //定义file的处理函数为fileHandler
|-->websUploadOpen(); //设置上传文件路径uploadDir = "/tmp",定义upload处理函数加入到handlers中
|-->websDefineHandler("upload", 0, uploadHandler, 0, 0); //定义upload的处理函数为uploadHandler
|-->websJstOpen(); //初始化hash结构websJstFunctions,定义write处理函数加入到websJstWrite中,定义jst处理函数加入到jstHandler中.
|-->websDefineJst("write", websJstWrite); //定义jst(javascript)的write的处理函数为websJstWrite,支持<% write("text"); %>
|-->websDefineHandler("jst", 0, jstHandler, closeJst, 0); //定义jst(javascript)的处理函数为jstHandler
|-->websOpenAuth(0); //初始化认证相关处理函数
|-->websDefineAction("userlogin", ActionUserLoginProc); //定义action=userlogin的处理函数为ActionUserLoginProc
|-->websDefineAction("userlogout", ActionUserLogoutProc); //定义action=userlogin的处理函数为ActionUserLoginProc
|-->verifyPassword = websVerifyPasswordFromFile; //设置验证密码函数
|-->websFsOpen(0); //初始化hash结构romFs,定义ME_ROM相关文件路径
|-->websLoad(routeFile); //load route.txt配置文件
|-->route = websAddRoute(uri, handler, -1) //将结构为WebsRoute的route加入hash结构routes中,初始化route中uri和handler
|-->websSetRouteMatch(route, dir, protocol, methods, extensions, abilities, redirects); //初始化route中protocol等
|-->websSetRouteAuth(route, auth) //初始化route中authType等
|-->for(websMimeList){hashEnter(websMime)} //初始化goahead默认支持的content type
|-->open(accessLog, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0666) //初始化access.log log日志文件
|-->websLoad(auth) //load auth.txt配置文件
|-->websAddRole(name, abilities) //增加管理角色,如role name=manager abilities=view,edit,delete
|--> websAddUser(username, password, roles) //增加管理用户,如user name=admin password=xx roles=manager,purchaser
---------------------------------------初始化过程结束-------------------------------------------
|-->logHeader(); //日志打印goahead基本配置信息
|-->websListen(endpoint) //初始化socket并监听
|-->socketParseAddress(endpoint, &ip, &port, &secure, 80); //解析监听ip 端口
|-->sid = socketListen(ip, port, websAccept, 0) //创建socket,并监听,初始化WebsSocket结构加入到socketlist中
|-->listens[listenMax++] = sid //保存socket的WebsSocket信息到listens中
|-->websGetBackground() //使用daemon(0, 0)设置后台运行
---------------------------------------主循环处理开始-------------------------------------------
|-->websServiceEvents(&finished); //while(!finished){}主循环
|-->socketSelect(-1, delay) //select socket
|-->nEvents = select(socketHighestFd + 1, (fd_set *) readFds, (fd_set *) writeFds, (fd_set *) exceptFds, &tv); //select socket
|-->socketProcess() //处理请求
|-->socketDoEvent(sp);
|-->socketAccept(sp); //accept新请求
|-->newSock = accept(sp->sock, addr, (Socklen*) &len) //accept 新socket
|-->nid = socketAlloc(sp->ip, sp->port, sp->accept, sp->flags); //分配并初始化新的WebsSocket,加入到socketList[nid]
|-->socketList[nid] ->sock = newSock ... //继续初始化新的socketList[nid]的WebsSocket结构
|-->(sp->accept)(nid, ipbuf, port, sp->sid) //调用websAccept函数, nid为新建请求的socketid, ipbuf为远端IP,port为远端port, sp->sid为本机监听的socketid
|-->wid = websAlloc(sid);wp=webs[wid]; //根据参数sid(即nid)分配并初始化新的Webs,加入到webs[wid]
|-->initWebs(wp, 0, 0); //初始化新的webs[wid]的Webs结构
|-->wp->state = WEBS_BEGIN; //初始化链接状态机初始值
|-->bufCreate(&wp->output); //初始化发送buf
|-->bufCreate(&wp->input); //初始化接收buf
|-->wp->listenSid = listenSid; ... //继续初始化新的webs[wid]的Webs结构
|-->wp->timeout = websStartEvent(PARSE_TIMEOUT, checkTimeout, (void*) wp); //设置wp的超时函数
|-->socketEvent(sid, SOCKET_READABLE, wp); //
|-->readEvent(wp); //一直调用readEvent读http和处理http,直至处理接收设置SOCKET_WRITABLE才可调用writeEvent
|-->nbytes = websRead(wp, (char*) rxbuf->endp, ME_GOAHEAD_LIMIT_BUFFER) //读取http报文
|-->websPump(wp); //根据wp->state状态机的值处理http报文
|-->case WEBS_BEGIN: parseIncoming(wp);
|-->parseFirstLine(wp); //处理http请求行,并保存到wp结构中
|-->parseHeaders(wp); //处理http请求头部,并保存到wp结构中
|-->wp->state = (wp->rxChunkState || wp->rxLen > 0) ? WEBS_CONTENT : WEBS_READY; //设置wp->state状态值
|-->websRouteRequest(wp); //设置此链接的处理函数
|-->case WEBS_CONTENT: processContent(wp);
|-->websProcessCgiData(wp); //设置此链接的处理函数jstHandler、cgiHandler、continueHandler、redirectHandler、actionHandler等
|-->case WEBS_READY: websRunRequest(wp);
|-->(*route->handler->service)(wp); //执行此链接的处理函数,
|-->hashLookup(actionTable, actionName);(*fn)((void*) wp); //以actionHandler为例,执行actionTable中对应函数,以ActionDefault为例
|-->ActionDefault
|-->websWrite_ex(webs, 0, json_object_to_json_string(obj),token); //返回页面数据
|-->jstHandler(Webs *wp); //以jstHandler为例,执行...
|-->...websDone(wp);
|-->websFlush(wp, 0); -->wp->state = WEBS_COMPLETE;
|-->socketCreateHandler(wp->sid, sp->handlerMask | SOCKET_WRITABLE, socketEvent, wp);
|-->case WEBS_RUNNING: /* Nothing to do until websDone is called */
|-->case WEBS_COMPLETE: complete(wp, 1);
|-->socketCreateHandler(wp->sid, sp->handlerMask | SOCKET_READABLE, socketEvent, wp); //初始化此链接的处理函数sp->handler=socketEvent
|-->writeEvent(wp);
|-->websFlush(wp, 0);
|-->while(websWriteSocket());
|-->wp->state = WEBS_COMPLETE;
|-->websFree(wp);
|-->(sp->handler)(sid, sp->handlerMask & sp->currentEvents, sp->handler_data) //
|-->websCgiPoll(); //如有cgi,只响应cgi
|-->websRunEvents(); //执行已加入定时器相关函数
|-->websClose(); //关闭相关资源
goahead的cgi是和主程序一起编译的生成一个可执行文件的,cgi越多也越会使goahead越不稳定。
在route.txt文件里定义了redirect的处理方法,如:
route uri=/cgi-bin dir=cgi-bin handler=cgi
route uri=/action handler=action
route uri=/upload methods=PUT|POST handler=upload
route uri=/ extensions=jst handler=jst
route uri=/ methods=OPTIONS|TRACE handler=options
goahead按照顺序依次检查客户端的请求,一旦请求path满足上面的条件后就会自动执行后面的cgi回调函数,当然,这些函数必须在goahead的初始化过程中进行注册,注册过程参看websOpen函数。
文件处理由fileHandler函数处理,具体参看该函数代码
form,action,cgi,Options都可以统归为一类,只要在router.txt中定义好处理方法就行了,注册如下(所有的注册都类似):
websDefineAction("your_actions_handle", your_actions_handle_fun);
goahead实现了一种动态网页技术,也就是按照一定的格式书写网页,goahead读取该格式网页的时候会先将其中对应地点的函数执行一遍再返回给客户端。在此不推荐使用这种方式,这将会导致goahead中注册极其多的处理函数。推荐前端先获取一个空框架的页面,再用ajax的方式和goahead通信来获取初始化数据。
系统注册了处理方法,初始化的过程读取了/etc/config/system中约定好的用户名和密码,
websDefineAction("userlogin", ActionUserLoginProc);
websDefineAction("userlogout", ActionUserLogoutProc);
详细可看源码 auth.c
本人开发的时候,是利用cookie来保护web服务器中指定的一些静态资源,具体在fileHandler的处理中验证浏览器发送过来的cookie,如果通过则返回对应的资源,如果不通过则强制跳转到登录页面。
至于goahead自身的验证机制就没细看了
【智能路由器】系列文章连接
http://blog.csdn.net/u012819339/article/category/5803489