Web Develop

关于Apache httpd 的SSI和cgi使用和思考

因为在单片机上的web服务是使用删减版的httpd服务器,只保留最原始的扩展功能,SSI和CGI。无法像pc上的服务器可以使用php,jsp之类的引擎。 SSI主要是为了往网页里面插入实时的变量,CGI主要是处理客户端发送来的请求。这样即使是最古老的浏览器也不会有什么兼容性问题。 但是这样的做法的结果是服务端的编程变繁琐了。作为单片机这种嵌入式web应用,应该尽可能工作量放在客户端来完成,减少服务端的复杂度。而且不需要太考虑兼容古老的浏览器,因为落后的东西就应该被淘汰。 早期的浏览器作为通用的客户端只拥有简单的功能,比如实现get和post等几个方法。大量的跳转和内容生成是在服务端运行的,就造成服务器的负载较大。自从javascript成为浏览器端标准的脚本语言后,各种网站的内容交互方式就变得更加自由了。我这里感受最深的就是使用客户端脚本把动态网页的生成工作放在浏览器端来做,数据由ajax 单独请求更新。嵌入式web经常需要显示一些实时的数据,把单独的数据请求放在后台实时更新,然后由脚本把它填写至网页。不像早期的技术使用SSI,服务端需要检索整个网页的SSI tag,然后查找变量,将数据实时更新至网页,然后发送整张网页。可以想服务端的压力有多大。其实这种思维模式就像网站程序和网站数据分开放一个道理。好处多多! 因为用单片机的web服务器调试前端代码会比较繁琐,要转码要下载,频繁修改就不方便。所以直接在pc的web服务器上调试前端代码,并且使用SSI和CGI,模仿单片机C后台的动作,这里要有一些技巧。 在Apache httpd web服务器上使用SSI和cgi,可以参考服务器主页上的帮助文档 http://httpd.apache.org/docs/current/howto/ssi.html。这里简单做个记录。 SSI 按照官方介绍把SSI功能打开。怎么设置httpd.conf就不多说。这里简单记录下其使用规则。 想设置某个SSI tag时,可以使用 [html] [/html] 在需要调用它的地方使用 [html] [/html] 就可以显示出 xxxx 了。 仅仅是这样使用,跟我们在使用最原始的SSI还是有点不一样。 为了方便调试,我们可以在页面上头加上 [html] [/html] 重定义这个未找到SSI时的提示语言,原本的提示语言很长,不方便调试。 CGI有几个要点: 1. CGI开启后,默认设置ScriptAlias是只执行cgi-bin下的脚本。你可以将自己的web目录添加进去,因为实际请求的路径不一定是/cgi-bin。 2. 这里可以用perl、python或者bash脚本来模仿单片机c的后端应答动作。这样调试好前端代码后,下进单片机后再慢慢调试后端代码就方便了。注意在perl前面要设置好perl.exe的路径。同时可以修改.pl后缀为我们想要的.cgi它也可以得到执行。完美模仿单片机后端逻辑! Get!

Http Authorization

Basic Authorization 从最初的基本的授权方式说起。 http头里添加:WWW-Authenticate: Basic realm="xxx” 即可要求浏览器输入授权信息。 浏览器发送时会附带授权信息:Authorization:Basic YWRtaW46YWRtaW4= 它是由base64加密的。如上解密后为:admin:admin Basic authorization 的授权方式就是每个请求中在http头里都携带着base64加密的授权信息,服务器在每次收到请求时都要进行权限判断,再执行对应的操作。 由于考虑到授权,那自然涉及到了客户端状态。http /0.9是无状态无连接的协议,一个特定的输入就对应一个特定的输出。由于http 不需要很强的实时性,属于一问一答的对话形式,且有时并发性强,不可能是一对一的专人(线程)负责一个会话。所以如果涉及到状态就不如把状态参数放置在请求中,服务端就要先对请求中携带的状态进行判断,再判断对应的操作是否有权限等等。按数字电路的理论,服务端的业务就变成两段式状态机的组合逻辑部分。状态部分可以保存在客户端或者服务端,这就变成了cookie和session了。 http /1.0增加了keep-alive 选项,http /1.1则默认了所有连接都有keep-alive选项。http keep-alive 选项就是说 发送完响应之后不服务端不主动关闭连接(http /0.9 是会主动关闭连接的)。http 的keep-alive 应该通过是tcp的keep-alive 定时心跳来维持的,不过http的keep-alive 还有一层意思是即使tcp 连接断开了,http的session还是keep alive的,http 可能也维护着一个保活定时器去试探每个连接上的客户端是否仍在线(这个不清楚)。 当http涉及到用户的概念时,就需要识别用户了,可能最早http识别一个客户端的方法跟标识一个tcp连接的方法是一样的,即IP+Port的形式来断定。然后变成 client 的(IP+Port+uid+password)的,也就是Basic Authorization的方式。状态信息可以放在url(GET),http header(authorizatin, cookie) 或者 post content里面。 对安全性的考虑: 由于每次都携带用户名和密码的这种请求方式极不安全,所以衍生版本就是用cookie来替代每次都携带的用户名和密码,这样就算cookie被人获取利用,也不会泄漏用户名和密码。只有在登陆的时候会传输一次用户名和密码。再者base64是可逆的加密方式,后面也使用不可逆的加密方式如md5(因为它是有损的加密,不可能还原出原密是什么)。但是如果被抓包,还是会被黑客冒名向服务器发出请求。所以早期网上支付等等就需要k宝,动态口令卡之类的东西。现在由于TLS/SSL等技术的出现,http on TLS/SSL 就很安全了。传输层或者socket层之上的信息全部使用密钥加双方证书加密,再靠抓包破解就很难了,所以这几年网络支付又火了起来。 PS: 看了一个wordpress登录的时候是直接POST 明文用户名加密码,好危险。 FAQ: Chrome delete authorization information 在使用 header(‘WWW-Authenticate: Basic realm="userlogin”'); 调试用户登录授权时,浏览器总是会保存之前输入的授权信息。想输入其它用户测试都不行。 所以有两个方法: 1. 使用隐私窗口调试。 2. 在url之前增加 “admin@” 就可以输入新的授权信息了。 php 的header函数,和html 中的标签效果的区别 两个都用来产生response header的,不同的是前者应该直接由服务端在http header里的添加值对。后者只是将值对放在html中传送,由浏览器来解析。这可以在浏览器中调试发现它们的区别。 Session and Cookie Session 是服务端维护的会话信息, cookie是用于标识客户对象和携带客户状态参数的变量。最初由客户端发送,服务端即认识了这个访问对象(可能还要加上ip+port信息),每次客户端有相应的操作之后,服务端即会把一些状态参数加入cookie中返回给客户端。这样服务端就不需保存用户操作状态了。session 保存着这次会话的信息,超时无操作之后就会结束这次会话。 以上纯属个人理解,因为目前在应用层的使用经验比较少,只是在嵌入式开发中涉及到一些初级的简单的应用层开发。属于http的一些最初的实现,只求简单可靠,服务端处理只有C语言。不过也有必要了解一下所谓“未来”的解决方案。有高级的框架的比如 jQuery.

HTTP协议与HTTP表单传输格式

HTTP请求 从使用者的角度看,一个HTTP请求起始于 用户端浏览器上输入的一个URL地址; 网页中的一个超链接; 提交一个HTML表单。 但本质上说,一个HTTP请求起始于用户端向HTTP服务器发送的一个URL请求。 一个标准的HTTP请求由以下几个部分组成 [code] [] [/code] 在HTTP请求中,第一行是请求行(request-line),用来说明请求类型、要访问的资源(URL)以及使用的HTTP版本; 紧接着是多行头部(headers)信息,用来说明服务器要使用的附加信息; 头部信息之后是一个回车换行符(/r/n),用于标明头部信息的结束。 以上是必须内容,根据需要可在头部信息结束之后增加主体数据(request-body); 主体数据之后是一个回车换行符(/r/n),用于标明主体数据的结束。 需要注意的是 请求行(request-line)中的URL部分必须以application/x-www-form-urlencoded方式编码。 主体数据(request-body)的编码方式由头部(headers)信息中的Content-Type指定。 主体数据(request-body)的长度由头部(headers)信息中的Content-Length指定。 例如,我们可以在IE浏览器上输入下面的网址: http://localhost:8000/hello/index.html HTTP请求的头部信息如下: [code] GET /hello/index.html HTTP/1.1 Accept: */* Accept-Language: zh-cn Accept-Encoding: gzip, deflate Host: localhost:8000 Connection: Keep-Alive Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6 [/code] 上述信息没有request-body部分,这是以GET方式发送的HTTP请求。如果请求中需要附加主体数据,即增加request-body部分,则必须使用POST方式发送HTTP请求。HTML超链接()只能用GET方式提交HTTP请求,HTML表单( )则可以使用两种方式提交HTTP请求。 HTML表单 HTML表单的使用方法如下: [html] … VALUE VALUE … [/html] 表单中存在各种类型的表单域标签,如 [html] 、及。 [/html] 每一种表单域标签均有NAME与VALUE两种标签属性。这两个标签属性决定了表单提交时传送的属性名及相应的值。 目标地址(URL) action标签属性指定了表单提交的目标地址,其值可以是完整的URL。如: [html] … 如果放置表单的网页与表单提交的目标地址在同一个HTTP服务器上,则目标地址可以用绝对路径表示(绝对路径相对于HTTP服务器)。绝对路径以“/”开头,包括WEB应用上下文及请求。如: 如果放置表单的网页与表单提交的目标地址在同一个WEB应用上下文上,则目标地址可以用相对路径表示(相对路径相对于放置表单的网页)。相对路径不以“/”开头,不包括WEB应用上下文。如: 需要注意的是,action标签属性的值必须符合URL的要求,其编码必须符合application/x-www-form-urlencoded编码规则。如下面的表单: … [/html] 这样的表单是不符合要求的。如果其URL值存在非法字符(如中文字符),应将其进行URL Encoding处理。URL Encoding的处理方法如下: 字母数字字符 “a” 到 “z”、“A” 到 “Z” 和 “0” 到 “9” 保持不变。 特殊字符 “.

SSI Tag的使用

这里先说最原始的使用SSI tag的方式,因为这样做在C开发中最简单。跟后来通用的web服务器的SSI有点不一样,因为后来为了使上层开发更方便,又进行了一些封装。 最原始的SSI是这样规定的,如在html中嵌入SSI tag。使用 [code]<–#tagname–>[/code] ;,则当Web服务器生成网页时,会将当前的标签后插入所指定的内容。这就是SSI Tag的作用。 SSI可以说是最初动态生成网页的方法,不仅仅是用来插入一些变量也可以直接插入javascript、json等等你想要发送给客户端的内容!一般用SSI来插入静态变量比较方便,如果是插入input框的当前值的话,可以往SSI中插入一段javascript代码或者是json格式的数据,再用javascript把这些实时的值加载进input框。 [html] Name:IP Address:MAC Address:[/html] 其Tag 在Web 服务端代码中作如下定义,并定义其序号的宏。 [c] static const char *g_pcConfigSSITags[] = { “ipaddr”, // SSI_INDEX_IPADDR “macaddr”, // SSI_INDEX_MACADDR “p0br”, // SSI_INDEX_P0BR “p0sb”, // SSI_INDEX_P0SB “p0p”, // SSI_INDEX_P0P “p0bc”, // SSI_INDEX_P0BC “p0fc”, // SSI_INDEX_P0FC “p0tt”, // SSI_INDEX_P0TT “p0tlp”, // SSI_INDEX_P0TLP “p0trp”, // SSI_INDEX_P0TRP “p0tip”, // SSI_INDEX_P0TIP “p0tip1”, // SSI_INDEX_P0TIP1 “p0tip2”, // SSI_INDEX_P0TIP2 “p0tip3”, // SSI_INDEX_P0TIP3 “p0tip4”, // SSI_INDEX_P0TIP4 “p0tnm”, // SSI_INDEX_P0TNM “p1br”, // SSI_INDEX_P1BR “p1sb”, // SSI_INDEX_P1SB “p1p”, // SSI_INDEX_P1P “p1bc”, // SSI_INDEX_P1BC “p1fc”, // SSI_INDEX_P1FC “p1tt”, // SSI_INDEX_P1TT “p1tlp”, // SSI_INDEX_P1TLP “p1trp”, // SSI_INDEX_P1TRP “p1tip”, // SSI_INDEX_P1TIP “p1tip1”, // SSI_INDEX_P1TIP1 “p1tip2”, // SSI_INDEX_P1TIP2 “p1tip3”, // SSI_INDEX_P1TIP3 “p1tip4”, // SSI_INDEX_P1TIP4 “p1tnm”, // SSI_INDEX_P1TNM “modname”, // SSI_INDEX_MODNAME “pnpport”, // SSI_INDEX_PNPPORT “disable”, // SSI_INDEX_DISABLE … }; [/c] 然后在SSIHandler里如下解析,当每次serving a page with a ``.