系列入口:编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客
本文介绍处理框架。
一、框架概述
处理框架针对的是一个连接,里面用了循环,支持HTTP1.1,如果不循环就是1.0了(1.1和1.0就这点区别)。
这个框架具有下列功能:
- 默认页,访问“/”会被重定向
- 基本认证
- 一系列的内置页面,后缀名为“asp”,嘿嘿,不要跟真正的asp混淆了
- 静态网页,因为支持静态网页,所以可以让前端帮忙设计css
因为主要目的是嵌入到已有的C++程序中,所以内置页面是我的重点,但作为普通服务器,有一个支持静态页面的功能就足够了。
续:
流程其实也是相当一目了然的。
二、保持连接
保持连接是HTTP1.1的特征,通过头标“Connection=Keep-Alive”(=后面有没有空格注意一下,可以从前两篇如何构造和分解头标的代码确认)指示。如果不打算保持连接,发送完毕后直接关闭连接即可。
三、基本认证
完整代码
//处理一个已经建立的连接virtual bool SocketProcess(CSocket & s, bool * pShutDown, long * pRet, long i_child, SocketServerControlBlock::T_CHILD_DATA * pThisProcessData){m_s=s;m_i_child = i_child;m_pThisChildData = pThisProcessData;//支持HTTP1.1,一个连接处理多个请求while(m_s.IsConnected() && !*pShutDown){bool isReady = false;pThisProcessData->SetHttpProcessInfo("wait...");if (!m_s.IsSocketReadReady(1, isReady)){LOG<<"socket error"<<ENDE;m_s.Close();return true;}if (!isReady){//DEBUG_LOG << "socekt not ready On HTTP Process" << ENDI;continue;}bool isKeepAlive=false;m_request.Clear();m_respond.Init();pThisProcessData->SetHttpProcessInfo("RecvRequest...");if(!m_request.RecvRequest(m_s)){if(m_s.IsConnected()){pThisProcessData->SetHttpProcessInfo("错误的请求");LOG << getpid() << "错误的请求:" << m_request.GetFullRequest() << ENDE;doPageBedRequest();m_s.Close();}else{pThisProcessData->SetHttpProcessInfo("连接已关闭");LOG<<getpid()<<"连接已关闭"<<ENDE;}break;}++pThisProcessData->request_count;LOG<<getpid()<<"接收到请求,接连信息:\n"<<m_s.debuginfo()<<ENDI;LOG<<getpid()<<"接收到请求,请求信息:\n"<<m_request.GetFullRequest()<<ENDI;pThisProcessData->SetHttpProcessInfo(m_request.GetResource().c_str());//检查是否需要保持连接if(m_request.GetHeader("Connection")=="Keep-Alive"){LOG<<getpid()<<"保持连接"<<ENDI;isKeepAlive=true;}else{LOG<<"不保持连接"<<ENDI;isKeepAlive=false;}if("/"==m_request.GetResource()){m_respond.Send302(m_s,"/login/login.htm");if(isKeepAlive)continue;else{m_s.Close();break;}}//处理用户认证,登录用的目录不需要认证,登录目录可以用XMLHttp来实现整合登录//login目录下的内容无需认证,这要求login下引用的图片也必须在login目录下if(NULL!=this->pfCheckUser){char const * logondir="/login/";if(0!=strncmp(logondir,m_request.GetResource().c_str(),strlen(logondir))){string user;string password;if(m_request.GetAuthorization(user,password)){if(!pfCheckUser(user.c_str(),password.c_str())){m_respond.Send401(m_s,m_pServerDatas->m_realm.c_str());if(isKeepAlive)continue;else{m_s.Close();break;}}string cookie="logon_user";if(m_request.GetCookie(cookie)!=user)m_respond.AddCookie(cookie,user);}else{m_respond.Send401(m_s,m_pServerDatas->m_realm.c_str());if(isKeepAlive)continue;else{m_s.Close();break;}}}}if("/default.asp"==m_request.GetResource() || "/default.htm"==m_request.GetResource()){OnPageStart("default");doPageDefault();OnPageEnd(false);}else if("/functionlist.asp"==m_request.GetResource()){OnPageStart("Function List");doPageFunctionList();OnPageEnd(false);}else if("/admin.asp"==m_request.GetResource()){doPageAdmin(pShutDown);}else if("/RegistSlave.asp"==m_request.GetResource()){doPageRegistSlive(pShutDown);}else if("/shell.asp"==m_request.GetResource()){OnPageStart("shell");doPageShell();OnPageEnd();m_s.Close();//所有此类页面都可能无法预先确定输出长度isKeepAlive=false;}else if("/ViewFile.asp"==m_request.GetResource()){char buf[2048];sprintf(buf,"查看文件 %s ",m_request.GetParam("file").c_str());OnPageStart(buf);doPageViewFile();OnPageEnd();m_s.Close();//所有此类页面都可能无法预先确定输出长度isKeepAlive=false;}else if("/stopserver.asp"==m_request.GetResource()){OnPageStart("stop server",true);if(NULL!=pfCheckAdmin && !pfCheckAdmin(m_request.GetParam("password").c_str())){m_respond.AppendBody("口令错误");OnPageEnd();}else{(*pShutDown) = true;m_respond.AppendBody("收到停止信号,服务正在停止......");m_respond.Flush(m_s);OnPageEnd();}isKeepAlive=false;m_s.Close();}else if("/DownFile.asp"==m_request.GetResource()){if(doPageFile(m_request.GetParam("file").c_str())){m_respond.Flush(s);}else{m_respond.Flush(s);m_s.Close();//所有此类页面都可能无法预先确定输出长度isKeepAlive=false;}}else if(m_request.GetResource().substr(0,5)=="/bin/" || m_request.GetResource().substr(0,7)=="/admin/"){//执行用户功能string resourcetype = m_request.GetResourceType();if (resourcetype == "asp" || resourcetype == "aspx" || resourcetype == "asmx"){doPageFunction();//内置页面}else{doPageCGI();//动态链接库实现的用户功能}}else{doPageFile();m_respond.Flush(s);}//客户指定不保持连接或应答不支持保持连接则关闭连接if(!isKeepAlive || !m_respond.isCanKeepAlive()){m_s.Close();break;}}return true;}
(我还没写完)