Go语言net/http标准库


$GOROOT\src\net
成熟Web框架使开发迅速和简便,开发者须接受框架的约定和模式,框架的约定和模式是最佳best practice,开发者不了解约定和模式,在不必要甚至有害的情况下盲目 使用,货物崇拜编程 cargo cult programming

货物崇拜编程

二战盟军对战事提供支援,太平洋多岛屿上设立空军基地,以空投向部队及支援部队的岛民投送大量生活用品及军事设备,
改善部队及岛民的生活,岛民因此看到人工衣物罐头食品以及其他物品,战后,空军基地被废弃,货物空投也停止了,岛民做了件非常符合本性的事,他们把自己打扮成空管员,士兵及水手,用机场上的指挥棒挥舞着陆信号,进行地面阅兵演习,试图让飞机继续空投货物,货物崇拜一词也因此而诞生.
复制粘贴StackOverflow 类网站上的代码,能运行,工作原理不了解 ,无法扩展和修改代码,货物崇拜程序员,会在不了解框架的模式或约定,不知道框架做何种取舍的情况下,盲目地使用框架

HTTP是无连接协议

connection-less protocol
HTTP发送给服务器的请求,对服务器之前处理过的请求一无所知,应用程序以cookie 在客户端实现数据持久化,
以会话在服务器上数据持久化,为降低使用cookie和会话带来的复杂性,框架提供统一接口,uniform interface,
不同框架的接口不同,会提供名字相同的接口,同名接口之间的实现各不相同,开发者困惑,用框架开发应用意味着将框架与应用绑定,
应用迁移至另一个框架,扩展应用或添加新的特性,都要对框架本身深入了解,要对框架进行定制,好的框架是快速构建可扩展且健壮应用的最好方法,理解框架的底层概念和基础设施非常重要,理清思路,不盲目地使用模式
Go语言框架net/http和html/template两个标准库,net/http标准库,分为客户端和服务器,库的结构和函数,
分别支持客户端和服务器,有些则同时支持客户端和服务器,
Client、Response、Header、Request和Cookie支持客户端,
Server、ServeMux、Handler/HandleFunc、ResponseWriter、Header、Request和Cookie支持服务器,
net/http标准库用作服务器,使用Go接收客户端的HTTP请求 Go,Web编程,主要关注net/http标准库的服务器功能,非客户端功能

Go构建服务器

net/http标准库启动HTTP服务器,让服务器接收请求并返回响应,
net/http标准库提供连接多路复用器multiplexe 的接口以及默认的多路复用器,
Go Web服务器
Go系列用于创建Web服务器的标准库,调用ListenAndServe 传入网络地址及负责处理请求的处理器 handler 作为参数,
网络地址为空字符串时服务器默认用80端口,
处理器参数为nil时服务器用默认的多路复用器 DefaultServeMux
package main
import( "net/http")
func main() {
 http.ListenAndServe("", nil)
 //server := http.Server{Addr:"127.0.0.1:8080",Handler: nil,}
 //server.ListenAndServe()
}
Server结构的配置选项
type Server struct {
  Addr      string
  Handler    Handler
  ReadTimeout  time.Duration
  WriteTimeout  time.Duration
  MaxHeaderBytes int
  TLSConfig   *tls.Config
  TLSNextProto  map[string]func(*Server, *tls.Conn, Handler)
  ConnState   func(net.Conn, ConnState)
  ErrorLog    *log.Logger
}

HTTPS服务

客户端和服务器要共享密码或信用卡信息,用HTTPS 加密和保护 客户端和服务器的通信,
支付卡行业数据安全标准 Payment Card Industry Data Security Standard 必须加密客户端和服务器的通信
Gmail和Facebook 带有隐私性质的网站 整个网站用HTTPS,用户登录功网站 也需要启用HTTPS
HTTPS 将HTTP通信放到SSL之上,通过ListenAndServeTLS函数,让Web应用提供HTTPS服务
package main
import ("net/http")
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: nil,
}
server.ListenAndServeTLS("cert.pem", "key.pem")
}
cert.pem 是SSL证书
key.pem 是服务器的私钥/private key
生产环境使用的SSL证书需要通过VeriSign、Thawte或者Comodo SSL这样的CA取得,
测试用证书和私钥,使用自行生成的证书就可以了,生成证书的办法有多种,其中一种是用Go标准库的crypto包群 library group

SSL TLS和HTTPS

Secure Socket Layer安全套接字层,
是一种通过PKI 公钥基础设施 Public Key Infrastructure 为通信双方提供数据加密和身份验证的协议,通信的双方通常是客户端和服务器
SSL由Netscape开发,后由IETF(Internet Engineering Task Force 互联网工程任务组)接手将其改名TLS为 Transport Layer Security 传输层安全协议)
HTTPS 即SSL之上的HTTP  就是在SSL/TLS连接的上层进行HTTP通信
HTTPS 使用SSL/TLS证书 实现数据加密以及身份验证
SSL证书存储在服务器之上 是一种使用X.509格式进行格式化的数据,数据包含了公钥以及其他一些相关信息,
为保证证书的可靠性,证书一般由证书分发机构(Certificate Authority,CA)签发,
服务器接收到客户端的请求后,将证书和响应返回给客户端,而客户端确认证书真实性后,会生成一个随机密钥(random key)
并使用证书中的公钥对随机密钥进行加密,加密产生的对称密钥 symmetric key 是客户端和服务器进行通信时负责对通信实施加密的实际密钥 actual key
自行生成的证书和私钥
了解SSL证书和私钥的生成方法,并学会在开发和测试的过程中使用证书和私钥
package main
import (
  "crypto/rand"
  "crypto/rsa"
  "crypto/x509"
  "crypto/x509/pkix"
  "encoding/pem"
  "math/big"
  "net"
  "os"
  "time"
)
func main() {
  max := new(big.Int).Lsh(big.NewInt(1), 128)
  serialNumber, _ := rand.Int(rand.Reader, max)
  subject := pkix.Name{
    Organization:    []string{"Manning Publications Co."},
    OrganizationalUnit: []string{"Books"},
    CommonName:     "Go Web Programming",
  }
  template := x509.Certificate{
    SerialNumber: serialNumber,
    Subject:   subject,
    NotBefore:  time.Now(),
    NotAfter:   time.Now().Add(365 * 24 * time.Hour),
    KeyUsage:   x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
  }
  pk, _ := rsa.GenerateKey(rand.Reader, 2048)
  derBytes, _ := x509.CreateCertificate(rand.Reader, &template,&template, &pk.PublicKey, pk)
  certOut, _ := os.Create("cert.pem")
  pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  certOut.Close()
  keyOut, _ := os.Create("key.pem")
  pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes:x509.MarshalPKCS1PrivateKey(pk)})
  keyOut.Close()
}
SSL证书 是一个将扩展密钥用法(extended key usage)设置成了服务器身份验证操作的X.509证书
程序在生成证书时使用了crypto/x509标准库
创建证书需要用到私钥 程序在使用私钥成功创建证书之后 会将私钥单独保存在一个存放服务器私钥的文件里面
程序使用一个 Certificate 结构来对证书进行配置
结构中的证书序列号 SerialNumber 用于记录由CA分发的唯一号码
为了能让 Web应用运行起来 程序在这里生成了一个非常长的随机整数来作为证书序列号
之后 程序创建了一个专有名称(distinguished name)
并将它设置成了证书的标题 subject
程序还将证书的有效期设置成了一年 而结构中KeyUsage字段和ExtKeyUsage字段的值则表明了这个X.509证书是用于进行服务器身份验证操作的
最后 程序将证书设置成了只能在IP地址127.0.0.1之上运行

X.509标准

国际电信联盟电信标准化部门(ITU-T)为公钥基础设施制定的一个标准  包含了公钥证书的标准格式
一个X.509证书(简称SSL证书)实际上就是一个经过编码的ASN.1(Abstract Syntax Notation One,抽象语法表示法/1)格式的电子文档
ASN.1既是一个标准 也是一种表示法 描述了表示电信以及计算机网络数据的规则和结构
X.509证书可以使用多种格式编码 其中一种编码格式是BER(Basic Encoding Rules,基本编码规则)
BER格式指定了一种自解释并且自定义的格式用于对ASN.1数据结构进行编码
而DER格式则是BER的一个子集 DER只提供了一种编码ASN.1值的方法 这种方法被广泛地应用于密码学当中 尤其是对X.509证书进行加密
SSL证书可以以多种不同的格式保存 其中一种是PEM(Privacy Enhanced Email,隐私增强邮件)格式
这种格式会对DER格式的X.509证书实施编码 并且这种格式的文件都以-----BEGIN CERTIFICATE-----开头 以-----END CERTIFICATE-----结尾
(除了用作文件格式之外 PEM和此处讨论的SSL证书关系并不大)
在此之后 程序通过调用crypto/rsa标准库中的GenerateKey函数生成了一个RSA私钥
pk, _ := rsa.GenerateKey(rand.Reader, 2048)
程序创建的RSA私钥的结构里面包含了一个能够公开访问的公钥(public key) 这个公钥在使用x509.CreateCertificate函数创建SSL证书的时候就会用到
derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template,&pk.PublicKey, pk)
CreateCertificate函数接受Certificate结构、公钥和私钥等多个参数
创建出一个经过DER编码格式化的字节切片。后续代码的意图也非常简单明了
首先使用encoding/pem标准库将证书编码到cert.pem文件里面
certOut, _ := os.Create("cert.pem")
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
然后继续以PEM编码的方式把之前生成的密钥编码并保存到key.pem文件里面:
keyOut, _ := os.Create("key.pem")
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes:x509.MarshalPKCS1PrivateKey(pk)})
keyOut.Close()
如果证书是由CA签发的 证书文件中将同时包含服务器签名以及CA签名 其中服务器签名在前 CA签名在后

Go语言 net/http和net-http相关