32 WWWBrowser—一个WWW客户端

一个充分利用所有方面的应用程序的好例子福吉特图书馆的是一个万维网客户端。它显示嵌入图像和GUI元素的超文本文档(到实施填表)。这些文件是从通过ftp、nntp等协议在互联网上提供信息源,gopher和http。

在本节中,我们将了解这样一个应用程序是如何实现的在Haskell的Fudget库上实现。一个实际的这个名为wwbrowser的实现主要在夏季完成1994年。1997年夏季进行了一些更新和改进窗口快照如所示图85.

图85.wwbrowser,一个简单的web浏览器,使用它支持内联图像和表单。

1994年版本的wwbrowser具有以下特性:

关于1994年实施的一些简要事实:1997年修改并增加了以下内容:

32.1当前WWW浏览器实现的总体结构

wwbrowser以直接的方式实现。关键数据类型是,毫不奇怪,统一资源定位地址Html格式。这些类型的关键操作是:

数据URL=。。。数据Html=。。。parseURL::String->可能的URLshowURL::URL->StringjoinURL::URL->URL->URLparseHtml::String->任意一个ErrorInfo HtmldrawHtmlDoc::URL->Html->HtmlDrawing类型HtmlDrawing=绘图。。。--详细信息第32.3条
文档是通过胡说八道urlFetchF,

urlFetchF::F HttpRequest HttpResponse--详细信息第32.2条

数据HttpRequest=HttpReq{reqURL::URL,…}数据HttpResponse=HttpResp{respBody::String,…}
笨蛋urlFetchF除了处理HTTP协议,但由于HTTP是WWW,它是第一个被实现的。无稽之谈对于其他协议,则使用相同的接口实现。

文件由福吉特展示HTML显示,

htmlDisplayF::F(URL,Html)HttpRequest
它显示在输入和输出上接收到的HTML文档当用户单击中的链接时请求新文档正在显示的文档。

并非WWW上的所有文档都是HTML文档。其他类型文档(例如纯文本、gopher页面、ftp目录列表和Usenet新闻文章)通过转换将它们转换为HTML:

toHtml::(URL,HttpResponse)->(URL,Html)
功能toHtml使用解析HTML以及其他解析器。

使用上述组件,我们可以创建一个简单的web浏览器

简单浏览器=循环(htmlDisplayF>==<mapF toHtml>==<urlFetchF)
但是除了HTML显示之外,WWWBrowser还提供了后退/前进按钮,网址输入栏,历史窗口,书签菜单,文档源窗口以及一个进度报告字段。主楼的结构如所示图86使用布局指定名称布局(请参见第11.2条).
WWW浏览器=httpMsgDispF>==<循环hrightf urlFetchF'mainGuiF>==<菜单哪里mainGuiF=urlInputF>*<srcDispF>*<(urlHistoryF>*<htmlDisplayF)>=^<toHtmlhttpMsgDispF=nameF“MsgDisp”$“进度:”`labLeftOfF`displayFurlefetchf'=post>^=<urlefetchf>=^<stripeany哪里发布消息=。。。urlInputF=…解析URL…stringInputF…showURL。。。srcDispF=。。。urlHistoryF=。。。

图86。WWW浏览器--主要的福吉进来了WWW浏览器。

32.2实施互联网协议

笨蛋urlFetchF实现为并行处理不同协议的混混。这是显示在图87.功能发行提取物请求URL中的协议字段,并将请求发送到适当的子预算。
urlFetchF::F HttpRequest HttpResponseurlFetchF=snd>^=<listF fetchers>=^<distr哪里取款机=[(“文件”,fileFetchF>=^<reqURL),--本地文件和ftp(“http”,httpFetchF),--http和gopher请求(“新闻”,newsFetchF>=^<reqURL),(“telnet”,telnetStarterF>=^<reqURL)]distr req@(HttpReq{reqURL=url})=(fetcher,req)哪里取数器=。。。

图87.福吉urlFetchF.

单个协议的实现fudget内核是以延续的风格写的。对于http协议,以下内容执行的操作:

  1. 在高级输入中接收请求。
  2. 提取URL的主机字段,并建立一个套接字连接对该主机已打开。如果使用代理,则连接到而是打开代理。
  3. 请求被发送到主机(或代理)。
  4. 将收到成批的答复(请参见第14.1条)以及组装好,连接关闭。
  5. 如果收到重定向响应,则进程将从重新启动步骤2和重定向URL。
  6. 如果接收到正常或错误响应,则将其放入在高级输出流中。
NNTP(news)协议的实现类似。A区别在于NNTP协议可以处理多个请求每个连接,因此请求后连接保持打开状态已完成,以便在下一个请求为指向同一主机。通常是这样的,因为你通常从同一本地新闻中获取所有新闻文章服务器。(有可能,但不常见,具体说明在URL中显式显示新闻服务器。)

FTP协议还可以处理每个连接,因为您需要先登录才能对文件的重用,甚至对传输文件更有利。

FTP协议的不同之处在于它使用控制连接用于发送启动文件传输和单独的数据连接每次文件传输。这个数据连接通常由服务器启动,连接到套接字由客户指定。在胡说八道实现,这两个连接由两个独立的,但是合作,福吉。

32.3显示HTML

HTML文档包含一系列元素,这些元素是由标记分隔。元素可以包含纯文本和其他,嵌套元素。例如,

<H1>fudget<TT>htmlDisplayF</TT></H1>
是标记为顶层标题的元素,并且它具有嵌套标记为用打字机字体显示的元素。

块级元素和文本级元素。以前的标记文本块被视为完整的段落。它们是这样组成的垂直地。标题元素是块级别的示例元素。后者标记任意字符序列在一个段落内。块级元素可以包含文本级元素(如上例所示),但反之亦然。

wwbrowser利用块级和文本级元素。这使得布局更容易。功能解析HTML顶层是块级元素的序列。纯文本发生在顶层,任何块级元素之外,被理解为发生在隐含段落中(<P>)元素。比如说,

<H1>fudget<TT>htmlDisplayF</TT></H1>实施。。。
与分析到相同的语法树中

<H1>fudget<TT>htmlDisplayF</TT></H1><P> 实施…</P>
通过这种方法,函数图纸HTMLDOC可以简单地递归到语法树下,组成使用垂直文本级元素使用段落.

网页不仅包含文本,还包含图像和表单元素。在WWWBrowser中,这些是通过嵌入画画里有福吉。我们介绍这个类型活动绘图对于包含激活元件和定义类型HTML绘图以上介绍为

类型HtmlDrawing=ActiveDrawing HtmlLabel Gfx HtmlInput HtmlOutput类型ActiveDrawing lbl leaf i o=绘图lbl(任一(F i o)叶)
哪里HTML输入HTML输出是消息类型吗被福吉特人用来实现图像和表单。元素特殊功能用类型的标签标记HTML标签。当前,超链接、链接目标、窗体和图像贴图已标记。

要显示的活动绘图s、 概括图形F(参见第27.5.1条)已定义:

动态图形SF::F(任意一个(GfxCommand(ActiveDrawing lbl leaf io))(Int,i))(GfxEvent(Int,o)之一)
笨蛋HTML显示使用活动图形SF显示HTML文档。它还包含

32.4并行获取图像

HTML文档中的图像是通过使用URL的引用包含的分别从它们的来源获取。笨蛋HTML显示用软糖图像获取F为此:

imageFetchF::F ImageReq(ImageReq,ImageResp)类型ImageReq=(URL,可能是大小)类型ImageResp=(大小,PixmapId)
由处理的请求图像获取F包含的URL要获取的图像和可选的所需大小图像应缩放。响应包含实际大小(缩放后)和pixmap标识符。

因为文档可能包含许多图像以及所需的时间获取图像通常由网络延迟控制与带宽限制相比,获取几个图像并行。笨蛋解析器服务器,

parServerF::Int->F req resp->F req resp
是一个用于创建可以处理多个并行请求。如果服务器F是个能应付的笨蛋按顺序请求请求和回应,然后是胡说八道解析器服务器 n 服务器F处理高达n请求平行的。

客户解析器服务器一定有办法告诉哪个响应属于哪个请求,因为响应的传递顺序是保证对应于接收请求的顺序。笨蛋图像获取F通过包含请求来实现这一点在回答中。

我们还希望避免获取同一图像两次。这是通过使用缓存来解决问题,

请求(请求)->缓存请求F(客户,请求)(客户,(请求,响应))
除了缓存响应,它还跟踪多个客户端,避免将同一请求发送两次到服务器即使两个客户机在同一时间发送相同的请求时间。(这种情况很容易在HTML显示,因为同一个图像经常出现在几个地方在同一个HTML文档中。)

在WWWBrowser中,这样的组合用于获取图像:

cacheF(parServerF 5 imageFetchF)
实施解析器服务器如所示图88实施缓存显示在里面图89.
parServerF::Int->F req resp->F req respparServerF n服务器=环通hrightf(absF ctrlSP0)服务器sf哪里serversF=listF[(i,serverF)| i<-ns]--n个并行服务器ns=[1..n]--服务器编号ctrlSP0=ctrlSP ns--ctrlSP的参数是当前可用服务器的列表ctrlSP服务器=案例服务器属于
          --如果所有服务器都忙,请等待响应。[]->getLeftSP$fromServer--如果有免费服务器:s: 服务器->getSP$fromServer fromClient哪里fromClient请求=--收到请求后,将其发送到
                --空闲列表中的第一个服务器并继续
                --因为还押服务器还在免费名单上。putSP(左(s,req))$ctrlSP服务器哪里从服务器(n,resp)=--当从服务器接收到响应时
          --输出它并将服务器添加到空闲列表中。putSP(右响应)$ctrlSP(n:服务器)

图88.福吉解析器服务器.

cacheF::Eq req=>F请求(req,resp)->F(客户机,请求)(客户机,(req,resp))cacheF serverF=loopthroughthrightf(absF(cacheSP[]])serverF缓存SP缓存挂起=getSP$answerFromServerSP请求fromclientsp哪里从客户端请求SP(n,req)=--来自客户n的请求。assoc oldSP newSP缓存请求哪里旧SP AN=--答案在缓存中找到了。putSP(右(n,(req,ans)))$缓存SP缓存挂起新闻=--一个新请求,发送到服务器,然后
                        --将客户端添加到挂起列表。
            如果req`elem`map snd挂起然后其他的putSP(左请求)cont哪里cont=缓存SP缓存((n,req):挂起)answerFromServerSP ans@(请求,)=--服务器发送了对请求请求的应答,
                --把它保存在缓存里,
                --将其转发给等待的客户端并将其从
                --待定列表。PUTSP[右(n,ans)|(n,uu)<-准备就绪]$cacheSP(ans:cache)挂起'哪里(=,准备就绪(=待处理部分)。snd)待定

图89.福吉特缓存。

32.5讨论

与其他现代浏览器相比,wwbrowser有一个缺点浏览器,它不会以增量方式显示文档它们是从网络接收的。这是由于以下几个事实:实现接收文档增量显示的一种方法会是让urlFetchF输出包含文档作为懒惰的一开始就列出字符从服务器接收。这将允许您应用解析器和绘图函数立即发送结果绘图到图形F。解析器解析HTML和绘图功能图纸HTMLDOC必须是精心设计,可以懒得产生一些输出即使只有输入的初始片段可用。你也必须改变图形F所以它不会从计算图形的大小开始。应该是这样延迟计算绘图命令(请参见第27.2条)发送到窗户系统。窗户的大小应该调整一下根据绘图命令生成的位置远已画好。

上述解决方案似乎需要引入一些机制的不确定性选择,因为当绘画正在计算和输出文档的命令,程序应该继续对其他输入做出反应。

然而,函数式语言的I/O系统的趋势是使I/O操作更加明确。即使是Fudget库已经放弃了流的表示,因为它是懒惰的支持更简单的确定性实现的列表。因此,在相反的方向。然而,程序的不确定性系统处于高抽象级别,流作为惰性列表似乎有用。

当然,人们可以想出实现增量的方法显示而不以延迟列表的形式进行输入。

但这似乎不是一个好的软件工程创建一个不同的解析库只是因为我们想使用在交互式程序中构造的解析器,但是从Haskell I/O背后的哲学来看,这似乎是我们要做的。

我们由此得出的结论是当前的I/O系统在哈斯凯尔,懒惰并不能很好地结合在一起。