JsonP原理:

原理:动态插入script标签,通过script标签引入js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且把我们需要的json数据作为参数传入。
由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名,协议,端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出Json数据并执行回调函数,从而解决了跨域的数据请求。
优点是兼容性好,简单易用,支持浏览器与服务器双向通信。缺点是只支持get请求。
JSONP:json+padding(内填充),顾名思义,就是把JSON填充到一个盒子里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createJs(sUrl){
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = sUrl;
document.getElementsByTagName('head')[0].appendChild(oScript);
}

createJs('jsonp.js');

box({
'name': 'test'
});

function box(json){
alert(json.name);
}

nginx实现跨域的原理及运行流程

原理:
首先,直接在浏览器地址栏中,输入某接口地址。是不会产生跨域问题的。
只有当在某域名的页面中,由该页面发起的接口请求。才可能会跨域。
nginx就类似于这个浏览器,它接收到外部对它的请求( 注意,nginx只会接收别人对它的请求,而不会拦截浏览器的请求 ),再类似浏览器地址栏一样去请求某个接口。最后将请求到的内容返回回去

前端利用host结合nginx实现跨域的运行流程:
Brower =》 host =》 nginx =》 目标地址
服务器数据 =》 nginx =》 Brower
也就是说,nginx并不是通过监听brower的请求。
而是作为一个服务器,接收外部对本机的请求。
所以是先通过host,让请求指向本机,才会经过nginx。才能进行转发。

TCP传输的三次握手四次挥手策略

先了解几个概念
ACK 确认序列编号
(表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0)
SYN 同步序列编号
(在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。)
FIN (finis)即完,终结的意思, 用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
TCP协议采用了三次握手策略。
用TCP协议把数据包送出去后,TCP不会对传送 后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志:SYN和ACK。

发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。
最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。
若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。
TCP三次握手的的好处在于:发送方可以确认接收方仍然在线,不会因为白发送而浪费资源。
断开一个TCP连接则需要“四次握手”:

第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。

第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。

第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。

第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

TCP和UDP的区别

TCP (Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须与对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来。
UDP (User Data Protocol,用户数据协议)是与TCP相对应的协议。它是基于面向非连接的协议,他不与对方建立连接,而是直接把数据包发过去!

常见web安全及防护原理

XSS原理及防范
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。比如:攻击者在论坛中放一个
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。

首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。

其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。

如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

尽量采用POST 而非GET 提交表单

XSS与CSRF有什么区别吗?
XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。
要完成一次CSRF攻击,受害者必须依次完成两个步骤:
登录受信任网站A,并在本地生成Cookie。
在不登出A的情况下,访问危险网站B。

CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。

  1. 提交验证码
    在表单中添加一个随机的数字或字母验证码。通过强制用户和应用进行交互。来有效地遏制CSRF攻击。

  2. Referer Check
    检查假设是非正常页面过来的请求,则极有可能是CSRF攻击。

  3. token验证

在 HTTP 请求中以參数的形式添加一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,假设请求中没有
token 或者 token 内容不对,则觉得可能是 CSRF 攻击而拒绝该请求。
token须要足够随机
敏感的操作应该使用POST。而不是GET,以form表单的形式提交。能够避免token泄露。

  1. 在 HTTP 头中自己定义属性并验证
    这样的方法也是使用 token 并进行验证。这里并非把 token 以參数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自己定义的属性里。通过 XMLHttpRequest 这个类,能够一次性给全部该类请求加上 csrftoken 这个 HTTP 头属性。并把 token 值放入当中。这样攻克了上种方法在请求中添加 token 的不便。同一时候,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用操心 token 会透过 Referer 泄露到其它站点中去。

栈和队列的区别?

栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。

队列先进先出,栈先进后出。

栈只允许在表尾一端进行插入和删除,而队列只允许在表尾一端进行插入,在表头一端进行删除

栈和堆的区别?

栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。

http1.0和http1.1的区别

主要区别主要体现在:

  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
  2. 宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

http2.0与http1.x的区别

1,新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
2,多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里
3,header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
4,服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。

defer和async

defer并行加载js文件,会按照页面上script标签的顺序执行
async并行加载js文件,下载完成立即执行,不会按照页面上script标签的顺序执行

说说你对闭包的理解

使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念

闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收

介绍一下box-sizing属性?

box-sizing属性主要用来控制元素的盒模型的解析模式。默认值是content-box。
content-box:让元素维持W3C的标准盒模型。元素的宽度/高度由border + padding + content的宽度/高度决定,设置width/height属性指的是content部分的宽/高
border-box:让元素维持IE传统盒模型(IE6以下版本和IE6~7的怪异模式)。设置width/height属性指的是border + padding + content

CSS3中新增了一种盒模型计算方式:box-sizing。盒模型默认的值是content-box, 新增的值是padding-box和border-box,几种盒模型计算元素宽高的区别如下:

content-box(默认)
布局所占宽度Width:

css
Width = width + padding-left + padding-right + border-left + border-right
布局所占高度Height:

css
Height = height + padding-top + padding-bottom + border-top + border-bottom
padding-box
布局所占宽度Width:

css
Width = width(包含padding-left + padding-right) + border-top + border-bottom
布局所占高度Height:

css
Height = height(包含padding-top + padding-bottom) + border-top + border-bottom
border-box
布局所占宽度Width:

css
Width = width(包含padding-left + padding-right + border-left + border-right)
布局所占高度Height:

css
Height = height(包含padding-top + padding-bottom + border-top + border-bottom)

对BFC规范的理解?

BFC,块级格式化上下文,一个创建了新的BFC的盒子是独立布局的,盒子里面的子元素的样式不会影响到外面的元素。在同一个BFC中的两个毗邻的块级盒在垂直方向(和布局方向有关系)的margin会发生折叠。

new操作符具体干了什么呢?

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

讲讲304缓存的原理

服务器首先产生ETag,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。
304是HTTP状态码,服务器用来标识这个文件没修改,不返回内容,浏览器在接收到个状态码后,会使用浏览器已缓存的文件
客户端请求一个页面(A)。 服务器返回页面A,并在给A加上一个ETag。 客户端展现该页面,并将页面连同ETag一起缓存。 客户再次请求页面A,并将上次请求时服务器返回的ETag一起传递给服务器。 服务器检查该ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304(未修改——Not Modified)和一个空的响应体。

排序算法

冒泡排序
每次比较相邻的两个数,如果后一个比前一个小,换位置。
时间复杂度:O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var arr = [3, 1, 4, 6, 5, 7, 2];

function bubbleSort(arr) {
var len = arr.length;
for (var i = len; i >= 2; --i) {
for (var j = 0; j < i - 1; j++) {
if (arr[j + 1] < arr[j]) {
var temp;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}

function bubbleSort2(arr) {
var len = arr.length;
for (var i = 0; i <= len - 1; i++) {
for (var j = 0; j <= len - i; j++) {
if (arr[j + 1] < arr[j]) {
var temp;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}

console.log(bubbleSort(arr));
console.log(bubbleSort2(arr));

快速排序
采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放到右边。
时间复杂度:O(nlog2(n))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var arr = [3, 1, 4, 6, 5, 7, 2];

function quickSort(arr) {
if(arr.length == 0) {
return []; // 返回空数组
}

var cIndex = Math.floor(arr.length / 2);
var c = arr.splice(cIndex, 1);
var l = [];
var r = [];

for (var i = 0; i < arr.length; i++) {
if(arr[i] < c) {
l.push(arr[i]);
} else {
r.push(arr[i]);
}
}

return quickSort(l).concat(c, quickSort(r));
}

console.log(quickSort(arr));

1.一次完整的HTTP请求所经历的7个步骤
HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:

  1. 建立TCP连接
    在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建 Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则, 只有低层协议建立之后才能,才能进行更层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。
  2. Web浏览器向Web服务器发送请求命令
    一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。
  3. Web浏览器发送请求头信息
    浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
  4. Web服务器应答
    客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
  5. Web服务器发送应答头信息
    正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
  6. Web服务器向浏览器发送数据
    Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
  7. Web服务器关闭TCP连接
    一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:
    Connection:keep-alive
    TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

箭头函数

可以让this指向固定化,这种特性很有利于封装回调函数
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
async/await 是写异步代码的新方式,以前的方法有回调函数和Promise。
async/await是基于Promise实现的,它不能用于普通的回调函数。async/await与Promise一样,是非阻塞的。
async/await使得异步代码看起来像同步代码,这正是它的魔力所在。

http2.0和http1.1的区别

多路复用
多路复用允许单一的http2连接同时发起多重的请求-响应信息
http性能优化的关键并不在于高带宽,而是低延迟,TCP连接会随着时间进行自我调谐,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度,这种调谐则称之为TCP慢启动,由于这种原因,让原本就具有突发性和短时性的http连接变得十分低效
http2通过让所有数据流共用同一个连接,可以更有效的使用TCP连接,让高带宽也能真正服务于http的性能提升
小总结:多路复用技术,单连接多资源的方式,减少服务端的链接压力,内存占用更少,连接吞吐量更大,由于减少TCP慢启动时间,提高传输的速度
因为所有的http2的请求都在一个TCP连接上,所以在http1中的自动化合并文件和Sprite合图等资源合并减少请求的优化手段对于http2来说是没有效果的

二进制分帧
http2在应用层和传输层之间增加一个二进制分帧层,http2会将所有传输的信息分割成更小的消息和帧,并对他们采用二进制格式的编码,其中http1的首部信息会被封装成Headers帧,而我们的request body则封装到Data帧里面

首部压缩
http请求和响应都是由状态行,请求/响应头部,消息主题三部分组成,一般而言,消息主题都会经过gzip压缩,或者本身传输的就是压缩后的二进制文件,但状态行和头部却没有经过任何压缩,直接以纯文本传输,浪费流量资源
原理:头部压缩需要在支持http2的浏览器和服务端之间,维护一份相同的静态字典,包含常见的头部名称与值的组合,维护一份相同的动态字典,可以动态的添加内容,支持基于静态哈夫曼码表的哈夫曼编码

http2支持服务器推送
服务端推送是一种在客户端请求之前发送数据的机制,当代网页使用了许多资源:html,样式表,脚本等,在http1.x中这些资源每一个都必须明确的请求,这可能是一个很慢的过程,因为服务器必须等待浏览器做的每一个请求,网络经常是空闲和未充分使用的
为了改善延迟,http2引入了server push,它允许服务端推送资源给浏览器,在浏览器明确请求之前,一个服务器经常知道一个页面需要更多的附加资源,在他响应浏览器第一个请求时,可以开始推送这些资源,这允许服务端去完全充分利用一个可能空闲的网络,改善页面加载的时间
有了http2的服务端推送,http1时代的内嵌资源的优化手段也变得没有意义了,使用服务端推送更高效,因为客户端可以缓存起来,甚至可以不同页面之间共享

并行双向字节流的请求和响应
在http2上,客户端和服务端可以把http消息分解成回不依赖的帧,然后乱序发送,最后再在另一端把她们重新组合起来,同一链接上可以有多个不同方向上的数据在传输,客户端可以一边乱序发送stream,也可以一边接收着服务端的响应,在服务端同理
把http消息分解为独立的帧,交错发送,然后在另一端重新组装是http2最重要的一项增强,这个机制会在整个web技术栈中引发一系列的连锁反应,从而带来巨大的性能提升,因为

可以并行交错的发送请求,请求之间互不影响
可以并行交错的发送响应,响应之间互不干扰
只使用同一个连接即可并行的发送多个请求和响应
消除不必要的延迟,从而减少页面加载的时间
也就是说‘域名分区’的优化手段对于http2来说是无用的,因为资源都是并行交错发送,且没有限制,不需要额外的多域名并行下载
http2的请求优先级
每个http2流里面有个优先值,这个优先值确定着客户端和服务端处理不同的流采取不同的优先级策略,高优先级的流应该优先发送,但又不是绝对的准守,可能又会引入首队阻塞的问题,高优先级的请求慢导致阻塞其他文件的交付,分配处理资源和客户端与服务器间的带宽,不同优先级的混合是必须的

https
http协议传输的数据都是未加密的,也就是明文的,因此使用http协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL协议用于对http协议传输的数据进行加密,从而诞生了https,现在的https使用的都是TSL协议

https在传输数据之前需要客户端和服务端之间进行一次握手,在握手的过程中将确立双方加密传输数据的密码信息,TSL/SSL协议不仅仅是一套加密传输的协议,TSL/SSL中使用了非对称加密,对称加密以及hash算法

握手过程:

浏览器将自己支持的一套加密规则发送给网站

网站从中选出一组加密算法和hash算法,并将自己的身份信息以证书的形式发回给浏览器,证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息

获得网站证书后浏览器要做以下工作

验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致)如果证书受信任,则浏览器栏里会显示一个小锁头,否则会给出证书不受信的提示
如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密
使用约定好的hash计算握手信息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站
网站接收浏览器发来的数据之后要做以下工作

使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手信息,并验证hash是否与浏览器发来的一致
使用密码加密一段握手信息,发送给浏览器
浏览器解密并计算握手信息的hash,如果与服务端发来的hash一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密

这里浏览器与网站互相发送加密的握手信息并验证,目的是为了保证双发都获得了一致的密码,并且可以正常的加密解密数据

其中非对称加密算法用于在握手过程中加密生成的密码,对称加密算法用于对真正传输的数据进行加密,而hash算法用于验证数据的完整性

由于浏览器生成的密码是整个数据加密的关键,因此在传输的时候使用了非对称加密算法对其进行加密,非对称加密算法会生成公钥和私钥,公钥只能用于加密数据,因此可以随意传输,而网站的私钥用于对数据进行解密,所以网站都会非常小心的保管自己的私钥,防止泄漏

TSL握手的过程中如果有任何错误,都会使加密连接断开,从而阻止了隐私数据的传输,正是由于https非常的安全,攻击者无法从中找到下手的地方,于是更多的是采用了假证书的手法来欺骗客户端,从而获取明文信息

转载请保持原始链接

原始链接: https://ru23.com/note/7e86d7fb.html