This bumps containerd to cf554d59dd96e459544748290eb9167f4bcde509 and
includes various fixes and updates the grpc package and types generated
for use.
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
| ... | ... |
@@ -244,7 +244,7 @@ RUN set -x \ |
| 244 | 244 |
&& rm -rf "$GOPATH" |
| 245 | 245 |
|
| 246 | 246 |
# Install containerd |
| 247 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 247 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 248 | 248 |
RUN set -x \ |
| 249 | 249 |
&& export GOPATH="$(mktemp -d)" \ |
| 250 | 250 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -191,7 +191,7 @@ RUN set -x \ |
| 191 | 191 |
&& rm -rf "$GOPATH" |
| 192 | 192 |
|
| 193 | 193 |
# Install containerd |
| 194 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 194 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 195 | 195 |
RUN set -x \ |
| 196 | 196 |
&& export GOPATH="$(mktemp -d)" \ |
| 197 | 197 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -200,7 +200,7 @@ RUN set -x \ |
| 200 | 200 |
&& rm -rf "$GOPATH" |
| 201 | 201 |
|
| 202 | 202 |
# Install containerd |
| 203 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 203 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 204 | 204 |
RUN set -x \ |
| 205 | 205 |
&& export GOPATH="$(mktemp -d)" \ |
| 206 | 206 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -85,7 +85,7 @@ RUN set -x \ |
| 85 | 85 |
&& rm -rf "$GOPATH" |
| 86 | 86 |
|
| 87 | 87 |
# Install containerd |
| 88 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 88 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 89 | 89 |
RUN set -x \ |
| 90 | 90 |
&& export GOPATH="$(mktemp -d)" \ |
| 91 | 91 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -215,7 +215,7 @@ RUN set -x \ |
| 215 | 215 |
&& rm -rf "$GOPATH" |
| 216 | 216 |
|
| 217 | 217 |
# Install containerd |
| 218 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 218 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 219 | 219 |
RUN set -x \ |
| 220 | 220 |
&& export GOPATH="$(mktemp -d)" \ |
| 221 | 221 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -208,7 +208,7 @@ RUN set -x \ |
| 208 | 208 |
&& rm -rf "$GOPATH" |
| 209 | 209 |
|
| 210 | 210 |
# Install containerd |
| 211 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 211 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 212 | 212 |
RUN set -x \ |
| 213 | 213 |
&& export GOPATH="$(mktemp -d)" \ |
| 214 | 214 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -68,7 +68,7 @@ RUN set -x \ |
| 68 | 68 |
&& rm -rf "$GOPATH" |
| 69 | 69 |
|
| 70 | 70 |
# Install containerd |
| 71 |
-ENV CONTAINERD_COMMIT 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 71 |
+ENV CONTAINERD_COMMIT cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 72 | 72 |
RUN set -x \ |
| 73 | 73 |
&& export GOPATH="$(mktemp -d)" \ |
| 74 | 74 |
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \ |
| ... | ... |
@@ -56,7 +56,7 @@ clone git github.com/mattn/go-sqlite3 v1.1.0 |
| 56 | 56 |
clone git github.com/tchap/go-patricia v2.1.0 |
| 57 | 57 |
clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 |
| 58 | 58 |
# forked golang.org/x/net package includes a patch for lazy loading trace templates |
| 59 |
-clone git golang.org/x/net 78cb2c067747f08b343f20614155233ab4ea2ad3 https://github.com/tonistiigi/net.git |
|
| 59 |
+clone git golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://github.com/tonistiigi/net.git |
|
| 60 | 60 |
clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git |
| 61 | 61 |
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3 |
| 62 | 62 |
clone git github.com/docker/go-connections v0.2.0 |
| ... | ... |
@@ -97,7 +97,7 @@ clone git github.com/pborman/uuid v1.0 |
| 97 | 97 |
# get desired notary commit, might also need to be updated in Dockerfile |
| 98 | 98 |
clone git github.com/docker/notary v0.3.0 |
| 99 | 99 |
|
| 100 |
-clone git google.golang.org/grpc a22b6611561e9f0a3e0919690dd2caf48f14c517 https://github.com/grpc/grpc-go.git |
|
| 100 |
+clone git google.golang.org/grpc ab0be5212fb225475f2087566eded7da5d727960 https://github.com/grpc/grpc-go.git |
|
| 101 | 101 |
clone git github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f |
| 102 | 102 |
clone git github.com/docker/go v1.5.1-1-1-gbaf439e |
| 103 | 103 |
clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c |
| ... | ... |
@@ -109,7 +109,7 @@ clone git github.com/seccomp/libseccomp-golang 60c9953736798c4a04e90d0f3da2f933d |
| 109 | 109 |
clone git github.com/coreos/go-systemd v4 |
| 110 | 110 |
clone git github.com/godbus/dbus v4.0.0 |
| 111 | 111 |
clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 |
| 112 |
-clone git github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3 |
|
| 112 |
+clone git github.com/golang/protobuf 3c84672111d91bb5ac31719e112f9f7126a0e26e |
|
| 113 | 113 |
|
| 114 | 114 |
# gelf logging driver deps |
| 115 | 115 |
clone git github.com/Graylog2/go-gelf aab2f594e4585d43468ac57287b0dece9d806883 |
| ... | ... |
@@ -136,7 +136,7 @@ clone git google.golang.org/cloud dae7e3d993bc3812a2185af60552bb6b847e52a0 https |
| 136 | 136 |
clone git github.com/docker/docker-credential-helpers v0.3.0 |
| 137 | 137 |
|
| 138 | 138 |
# containerd |
| 139 |
-clone git github.com/docker/containerd 57b7c3da915ebe943bd304c00890959b191e5264 |
|
| 139 |
+clone git github.com/docker/containerd cf554d59dd96e459544748290eb9167f4bcde509 |
|
| 140 | 140 |
|
| 141 | 141 |
# cli |
| 142 | 142 |
clone git github.com/spf13/cobra 75205f23b3ea70dc7ae5e900d074e010c23c37e9 https://github.com/dnephin/cobra.git |
| ... | ... |
@@ -115,14 +115,17 @@ func (*UpdateProcessResponse) ProtoMessage() {}
|
| 115 | 115 |
func (*UpdateProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
| 116 | 116 |
|
| 117 | 117 |
type CreateContainerRequest struct {
|
| 118 |
- Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 119 |
- BundlePath string `protobuf:"bytes,2,opt,name=bundlePath" json:"bundlePath,omitempty"` |
|
| 120 |
- Checkpoint string `protobuf:"bytes,3,opt,name=checkpoint" json:"checkpoint,omitempty"` |
|
| 121 |
- Stdin string `protobuf:"bytes,4,opt,name=stdin" json:"stdin,omitempty"` |
|
| 122 |
- Stdout string `protobuf:"bytes,5,opt,name=stdout" json:"stdout,omitempty"` |
|
| 123 |
- Stderr string `protobuf:"bytes,6,opt,name=stderr" json:"stderr,omitempty"` |
|
| 124 |
- Labels []string `protobuf:"bytes,7,rep,name=labels" json:"labels,omitempty"` |
|
| 125 |
- NoPivotRoot bool `protobuf:"varint,8,opt,name=noPivotRoot" json:"noPivotRoot,omitempty"` |
|
| 118 |
+ Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 119 |
+ BundlePath string `protobuf:"bytes,2,opt,name=bundlePath" json:"bundlePath,omitempty"` |
|
| 120 |
+ Checkpoint string `protobuf:"bytes,3,opt,name=checkpoint" json:"checkpoint,omitempty"` |
|
| 121 |
+ Stdin string `protobuf:"bytes,4,opt,name=stdin" json:"stdin,omitempty"` |
|
| 122 |
+ Stdout string `protobuf:"bytes,5,opt,name=stdout" json:"stdout,omitempty"` |
|
| 123 |
+ Stderr string `protobuf:"bytes,6,opt,name=stderr" json:"stderr,omitempty"` |
|
| 124 |
+ Labels []string `protobuf:"bytes,7,rep,name=labels" json:"labels,omitempty"` |
|
| 125 |
+ NoPivotRoot bool `protobuf:"varint,8,opt,name=noPivotRoot" json:"noPivotRoot,omitempty"` |
|
| 126 |
+ Runtime string `protobuf:"bytes,9,opt,name=runtime" json:"runtime,omitempty"` |
|
| 127 |
+ RuntimeArgs []string `protobuf:"bytes,10,rep,name=runtimeArgs" json:"runtimeArgs,omitempty"` |
|
| 128 |
+ CheckpointDir string `protobuf:"bytes,11,opt,name=checkpointDir" json:"checkpointDir,omitempty"` |
|
| 126 | 129 |
} |
| 127 | 130 |
|
| 128 | 131 |
func (m *CreateContainerRequest) Reset() { *m = CreateContainerRequest{} }
|
| ... | ... |
@@ -233,8 +236,9 @@ func (*AddProcessResponse) ProtoMessage() {}
|
| 233 | 233 |
func (*AddProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
|
| 234 | 234 |
|
| 235 | 235 |
type CreateCheckpointRequest struct {
|
| 236 |
- Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 237 |
- Checkpoint *Checkpoint `protobuf:"bytes,2,opt,name=checkpoint" json:"checkpoint,omitempty"` |
|
| 236 |
+ Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 237 |
+ Checkpoint *Checkpoint `protobuf:"bytes,2,opt,name=checkpoint" json:"checkpoint,omitempty"` |
|
| 238 |
+ CheckpointDir string `protobuf:"bytes,3,opt,name=checkpointDir" json:"checkpointDir,omitempty"` |
|
| 238 | 239 |
} |
| 239 | 240 |
|
| 240 | 241 |
func (m *CreateCheckpointRequest) Reset() { *m = CreateCheckpointRequest{} }
|
| ... | ... |
@@ -258,8 +262,9 @@ func (*CreateCheckpointResponse) ProtoMessage() {}
|
| 258 | 258 |
func (*CreateCheckpointResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
|
| 259 | 259 |
|
| 260 | 260 |
type DeleteCheckpointRequest struct {
|
| 261 |
- Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 262 |
- Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` |
|
| 261 |
+ Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 262 |
+ Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` |
|
| 263 |
+ CheckpointDir string `protobuf:"bytes,3,opt,name=checkpointDir" json:"checkpointDir,omitempty"` |
|
| 263 | 264 |
} |
| 264 | 265 |
|
| 265 | 266 |
func (m *DeleteCheckpointRequest) Reset() { *m = DeleteCheckpointRequest{} }
|
| ... | ... |
@@ -276,7 +281,8 @@ func (*DeleteCheckpointResponse) ProtoMessage() {}
|
| 276 | 276 |
func (*DeleteCheckpointResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
|
| 277 | 277 |
|
| 278 | 278 |
type ListCheckpointRequest struct {
|
| 279 |
- Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 279 |
+ Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` |
|
| 280 |
+ CheckpointDir string `protobuf:"bytes,2,opt,name=checkpointDir" json:"checkpointDir,omitempty"` |
|
| 280 | 281 |
} |
| 281 | 282 |
|
| 282 | 283 |
func (m *ListCheckpointRequest) Reset() { *m = ListCheckpointRequest{} }
|
| ... | ... |
@@ -446,16 +452,17 @@ func (m *UpdateContainerRequest) GetResources() *UpdateResource {
|
| 446 | 446 |
} |
| 447 | 447 |
|
| 448 | 448 |
type UpdateResource struct {
|
| 449 |
- BlkioWeight uint32 `protobuf:"varint,1,opt,name=blkioWeight" json:"blkioWeight,omitempty"` |
|
| 450 |
- CpuShares uint32 `protobuf:"varint,2,opt,name=cpuShares" json:"cpuShares,omitempty"` |
|
| 451 |
- CpuPeriod uint32 `protobuf:"varint,3,opt,name=cpuPeriod" json:"cpuPeriod,omitempty"` |
|
| 452 |
- CpuQuota uint32 `protobuf:"varint,4,opt,name=cpuQuota" json:"cpuQuota,omitempty"` |
|
| 453 |
- CpusetCpus string `protobuf:"bytes,5,opt,name=cpusetCpus" json:"cpusetCpus,omitempty"` |
|
| 454 |
- CpusetMems string `protobuf:"bytes,6,opt,name=cpusetMems" json:"cpusetMems,omitempty"` |
|
| 455 |
- MemoryLimit uint32 `protobuf:"varint,7,opt,name=memoryLimit" json:"memoryLimit,omitempty"` |
|
| 456 |
- MemorySwap uint32 `protobuf:"varint,8,opt,name=memorySwap" json:"memorySwap,omitempty"` |
|
| 457 |
- MemoryReservation uint32 `protobuf:"varint,9,opt,name=memoryReservation" json:"memoryReservation,omitempty"` |
|
| 458 |
- KernelMemoryLimit uint32 `protobuf:"varint,10,opt,name=kernelMemoryLimit" json:"kernelMemoryLimit,omitempty"` |
|
| 449 |
+ BlkioWeight uint32 `protobuf:"varint,1,opt,name=blkioWeight" json:"blkioWeight,omitempty"` |
|
| 450 |
+ CpuShares uint32 `protobuf:"varint,2,opt,name=cpuShares" json:"cpuShares,omitempty"` |
|
| 451 |
+ CpuPeriod uint32 `protobuf:"varint,3,opt,name=cpuPeriod" json:"cpuPeriod,omitempty"` |
|
| 452 |
+ CpuQuota uint32 `protobuf:"varint,4,opt,name=cpuQuota" json:"cpuQuota,omitempty"` |
|
| 453 |
+ CpusetCpus string `protobuf:"bytes,5,opt,name=cpusetCpus" json:"cpusetCpus,omitempty"` |
|
| 454 |
+ CpusetMems string `protobuf:"bytes,6,opt,name=cpusetMems" json:"cpusetMems,omitempty"` |
|
| 455 |
+ MemoryLimit uint32 `protobuf:"varint,7,opt,name=memoryLimit" json:"memoryLimit,omitempty"` |
|
| 456 |
+ MemorySwap uint32 `protobuf:"varint,8,opt,name=memorySwap" json:"memorySwap,omitempty"` |
|
| 457 |
+ MemoryReservation uint32 `protobuf:"varint,9,opt,name=memoryReservation" json:"memoryReservation,omitempty"` |
|
| 458 |
+ KernelMemoryLimit uint32 `protobuf:"varint,10,opt,name=kernelMemoryLimit" json:"kernelMemoryLimit,omitempty"` |
|
| 459 |
+ KernelTCPMemoryLimit uint32 `protobuf:"varint,11,opt,name=kernelTCPMemoryLimit" json:"kernelTCPMemoryLimit,omitempty"` |
|
| 459 | 460 |
} |
| 460 | 461 |
|
| 461 | 462 |
func (m *UpdateResource) Reset() { *m = UpdateResource{} }
|
| ... | ... |
@@ -495,14 +502,14 @@ func (*Event) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} }
|
| 495 | 495 |
|
| 496 | 496 |
type NetworkStats struct {
|
| 497 | 497 |
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` |
| 498 |
- RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes" json:"rx_bytes,omitempty"` |
|
| 499 |
- Rx_Packets uint64 `protobuf:"varint,3,opt,name=rx_Packets" json:"rx_Packets,omitempty"` |
|
| 500 |
- RxErrors uint64 `protobuf:"varint,4,opt,name=Rx_errors" json:"Rx_errors,omitempty"` |
|
| 501 |
- RxDropped uint64 `protobuf:"varint,5,opt,name=Rx_dropped" json:"Rx_dropped,omitempty"` |
|
| 502 |
- TxBytes uint64 `protobuf:"varint,6,opt,name=Tx_bytes" json:"Tx_bytes,omitempty"` |
|
| 503 |
- TxPackets uint64 `protobuf:"varint,7,opt,name=Tx_packets" json:"Tx_packets,omitempty"` |
|
| 504 |
- TxErrors uint64 `protobuf:"varint,8,opt,name=Tx_errors" json:"Tx_errors,omitempty"` |
|
| 505 |
- TxDropped uint64 `protobuf:"varint,9,opt,name=Tx_dropped" json:"Tx_dropped,omitempty"` |
|
| 498 |
+ RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes,json=rxBytes" json:"rx_bytes,omitempty"` |
|
| 499 |
+ Rx_Packets uint64 `protobuf:"varint,3,opt,name=rx_Packets,json=rxPackets" json:"rx_Packets,omitempty"` |
|
| 500 |
+ RxErrors uint64 `protobuf:"varint,4,opt,name=Rx_errors,json=rxErrors" json:"Rx_errors,omitempty"` |
|
| 501 |
+ RxDropped uint64 `protobuf:"varint,5,opt,name=Rx_dropped,json=rxDropped" json:"Rx_dropped,omitempty"` |
|
| 502 |
+ TxBytes uint64 `protobuf:"varint,6,opt,name=Tx_bytes,json=txBytes" json:"Tx_bytes,omitempty"` |
|
| 503 |
+ TxPackets uint64 `protobuf:"varint,7,opt,name=Tx_packets,json=txPackets" json:"Tx_packets,omitempty"` |
|
| 504 |
+ TxErrors uint64 `protobuf:"varint,8,opt,name=Tx_errors,json=txErrors" json:"Tx_errors,omitempty"` |
|
| 505 |
+ TxDropped uint64 `protobuf:"varint,9,opt,name=Tx_dropped,json=txDropped" json:"Tx_dropped,omitempty"` |
|
| 506 | 506 |
} |
| 507 | 507 |
|
| 508 | 508 |
func (m *NetworkStats) Reset() { *m = NetworkStats{} }
|
| ... | ... |
@@ -511,10 +518,10 @@ func (*NetworkStats) ProtoMessage() {}
|
| 511 | 511 |
func (*NetworkStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
|
| 512 | 512 |
|
| 513 | 513 |
type CpuUsage struct {
|
| 514 |
- TotalUsage uint64 `protobuf:"varint,1,opt,name=total_usage" json:"total_usage,omitempty"` |
|
| 515 |
- PercpuUsage []uint64 `protobuf:"varint,2,rep,name=percpu_usage" json:"percpu_usage,omitempty"` |
|
| 516 |
- UsageInKernelmode uint64 `protobuf:"varint,3,opt,name=usage_in_kernelmode" json:"usage_in_kernelmode,omitempty"` |
|
| 517 |
- UsageInUsermode uint64 `protobuf:"varint,4,opt,name=usage_in_usermode" json:"usage_in_usermode,omitempty"` |
|
| 514 |
+ TotalUsage uint64 `protobuf:"varint,1,opt,name=total_usage,json=totalUsage" json:"total_usage,omitempty"` |
|
| 515 |
+ PercpuUsage []uint64 `protobuf:"varint,2,rep,name=percpu_usage,json=percpuUsage" json:"percpu_usage,omitempty"` |
|
| 516 |
+ UsageInKernelmode uint64 `protobuf:"varint,3,opt,name=usage_in_kernelmode,json=usageInKernelmode" json:"usage_in_kernelmode,omitempty"` |
|
| 517 |
+ UsageInUsermode uint64 `protobuf:"varint,4,opt,name=usage_in_usermode,json=usageInUsermode" json:"usage_in_usermode,omitempty"` |
|
| 518 | 518 |
} |
| 519 | 519 |
|
| 520 | 520 |
func (m *CpuUsage) Reset() { *m = CpuUsage{} }
|
| ... | ... |
@@ -524,8 +531,8 @@ func (*CpuUsage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31
|
| 524 | 524 |
|
| 525 | 525 |
type ThrottlingData struct {
|
| 526 | 526 |
Periods uint64 `protobuf:"varint,1,opt,name=periods" json:"periods,omitempty"` |
| 527 |
- ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods" json:"throttled_periods,omitempty"` |
|
| 528 |
- ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time" json:"throttled_time,omitempty"` |
|
| 527 |
+ ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods,json=throttledPeriods" json:"throttled_periods,omitempty"` |
|
| 528 |
+ ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time,json=throttledTime" json:"throttled_time,omitempty"` |
|
| 529 | 529 |
} |
| 530 | 530 |
|
| 531 | 531 |
func (m *ThrottlingData) Reset() { *m = ThrottlingData{} }
|
| ... | ... |
@@ -534,9 +541,9 @@ func (*ThrottlingData) ProtoMessage() {}
|
| 534 | 534 |
func (*ThrottlingData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} }
|
| 535 | 535 |
|
| 536 | 536 |
type CpuStats struct {
|
| 537 |
- CpuUsage *CpuUsage `protobuf:"bytes,1,opt,name=cpu_usage" json:"cpu_usage,omitempty"` |
|
| 538 |
- ThrottlingData *ThrottlingData `protobuf:"bytes,2,opt,name=throttling_data" json:"throttling_data,omitempty"` |
|
| 539 |
- SystemUsage uint64 `protobuf:"varint,3,opt,name=system_usage" json:"system_usage,omitempty"` |
|
| 537 |
+ CpuUsage *CpuUsage `protobuf:"bytes,1,opt,name=cpu_usage,json=cpuUsage" json:"cpu_usage,omitempty"` |
|
| 538 |
+ ThrottlingData *ThrottlingData `protobuf:"bytes,2,opt,name=throttling_data,json=throttlingData" json:"throttling_data,omitempty"` |
|
| 539 |
+ SystemUsage uint64 `protobuf:"varint,3,opt,name=system_usage,json=systemUsage" json:"system_usage,omitempty"` |
|
| 540 | 540 |
} |
| 541 | 541 |
|
| 542 | 542 |
func (m *CpuStats) Reset() { *m = CpuStats{} }
|
| ... | ... |
@@ -570,7 +577,7 @@ func (*PidsStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3
|
| 570 | 570 |
|
| 571 | 571 |
type MemoryData struct {
|
| 572 | 572 |
Usage uint64 `protobuf:"varint,1,opt,name=usage" json:"usage,omitempty"` |
| 573 |
- MaxUsage uint64 `protobuf:"varint,2,opt,name=max_usage" json:"max_usage,omitempty"` |
|
| 573 |
+ MaxUsage uint64 `protobuf:"varint,2,opt,name=max_usage,json=maxUsage" json:"max_usage,omitempty"` |
|
| 574 | 574 |
Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt" json:"failcnt,omitempty"` |
| 575 | 575 |
Limit uint64 `protobuf:"varint,4,opt,name=limit" json:"limit,omitempty"` |
| 576 | 576 |
} |
| ... | ... |
@@ -583,8 +590,8 @@ func (*MemoryData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{
|
| 583 | 583 |
type MemoryStats struct {
|
| 584 | 584 |
Cache uint64 `protobuf:"varint,1,opt,name=cache" json:"cache,omitempty"` |
| 585 | 585 |
Usage *MemoryData `protobuf:"bytes,2,opt,name=usage" json:"usage,omitempty"` |
| 586 |
- SwapUsage *MemoryData `protobuf:"bytes,3,opt,name=swap_usage" json:"swap_usage,omitempty"` |
|
| 587 |
- KernelUsage *MemoryData `protobuf:"bytes,4,opt,name=kernel_usage" json:"kernel_usage,omitempty"` |
|
| 586 |
+ SwapUsage *MemoryData `protobuf:"bytes,3,opt,name=swap_usage,json=swapUsage" json:"swap_usage,omitempty"` |
|
| 587 |
+ KernelUsage *MemoryData `protobuf:"bytes,4,opt,name=kernel_usage,json=kernelUsage" json:"kernel_usage,omitempty"` |
|
| 588 | 588 |
Stats map[string]uint64 `protobuf:"bytes,5,rep,name=stats" json:"stats,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` |
| 589 | 589 |
} |
| 590 | 590 |
|
| ... | ... |
@@ -634,14 +641,14 @@ func (*BlkioStatsEntry) ProtoMessage() {}
|
| 634 | 634 |
func (*BlkioStatsEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} }
|
| 635 | 635 |
|
| 636 | 636 |
type BlkioStats struct {
|
| 637 |
- IoServiceBytesRecursive []*BlkioStatsEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive" json:"io_service_bytes_recursive,omitempty"` |
|
| 638 |
- IoServicedRecursive []*BlkioStatsEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive" json:"io_serviced_recursive,omitempty"` |
|
| 639 |
- IoQueuedRecursive []*BlkioStatsEntry `protobuf:"bytes,3,rep,name=io_queued_recursive" json:"io_queued_recursive,omitempty"` |
|
| 640 |
- IoServiceTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive" json:"io_service_time_recursive,omitempty"` |
|
| 641 |
- IoWaitTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive" json:"io_wait_time_recursive,omitempty"` |
|
| 642 |
- IoMergedRecursive []*BlkioStatsEntry `protobuf:"bytes,6,rep,name=io_merged_recursive" json:"io_merged_recursive,omitempty"` |
|
| 643 |
- IoTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,7,rep,name=io_time_recursive" json:"io_time_recursive,omitempty"` |
|
| 644 |
- SectorsRecursive []*BlkioStatsEntry `protobuf:"bytes,8,rep,name=sectors_recursive" json:"sectors_recursive,omitempty"` |
|
| 637 |
+ IoServiceBytesRecursive []*BlkioStatsEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive,json=ioServiceBytesRecursive" json:"io_service_bytes_recursive,omitempty"` |
|
| 638 |
+ IoServicedRecursive []*BlkioStatsEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive,json=ioServicedRecursive" json:"io_serviced_recursive,omitempty"` |
|
| 639 |
+ IoQueuedRecursive []*BlkioStatsEntry `protobuf:"bytes,3,rep,name=io_queued_recursive,json=ioQueuedRecursive" json:"io_queued_recursive,omitempty"` |
|
| 640 |
+ IoServiceTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive,json=ioServiceTimeRecursive" json:"io_service_time_recursive,omitempty"` |
|
| 641 |
+ IoWaitTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive,json=ioWaitTimeRecursive" json:"io_wait_time_recursive,omitempty"` |
|
| 642 |
+ IoMergedRecursive []*BlkioStatsEntry `protobuf:"bytes,6,rep,name=io_merged_recursive,json=ioMergedRecursive" json:"io_merged_recursive,omitempty"` |
|
| 643 |
+ IoTimeRecursive []*BlkioStatsEntry `protobuf:"bytes,7,rep,name=io_time_recursive,json=ioTimeRecursive" json:"io_time_recursive,omitempty"` |
|
| 644 |
+ SectorsRecursive []*BlkioStatsEntry `protobuf:"bytes,8,rep,name=sectors_recursive,json=sectorsRecursive" json:"sectors_recursive,omitempty"` |
|
| 645 | 645 |
} |
| 646 | 646 |
|
| 647 | 647 |
func (m *BlkioStats) Reset() { *m = BlkioStats{} }
|
| ... | ... |
@@ -707,7 +714,7 @@ func (m *BlkioStats) GetSectorsRecursive() []*BlkioStatsEntry {
|
| 707 | 707 |
|
| 708 | 708 |
type HugetlbStats struct {
|
| 709 | 709 |
Usage uint64 `protobuf:"varint,1,opt,name=usage" json:"usage,omitempty"` |
| 710 |
- MaxUsage uint64 `protobuf:"varint,2,opt,name=max_usage" json:"max_usage,omitempty"` |
|
| 710 |
+ MaxUsage uint64 `protobuf:"varint,2,opt,name=max_usage,json=maxUsage" json:"max_usage,omitempty"` |
|
| 711 | 711 |
Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt" json:"failcnt,omitempty"` |
| 712 | 712 |
Limit uint64 `protobuf:"varint,4,opt,name=limit" json:"limit,omitempty"` |
| 713 | 713 |
} |
| ... | ... |
@@ -718,11 +725,11 @@ func (*HugetlbStats) ProtoMessage() {}
|
| 718 | 718 |
func (*HugetlbStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} }
|
| 719 | 719 |
|
| 720 | 720 |
type CgroupStats struct {
|
| 721 |
- CpuStats *CpuStats `protobuf:"bytes,1,opt,name=cpu_stats" json:"cpu_stats,omitempty"` |
|
| 722 |
- MemoryStats *MemoryStats `protobuf:"bytes,2,opt,name=memory_stats" json:"memory_stats,omitempty"` |
|
| 723 |
- BlkioStats *BlkioStats `protobuf:"bytes,3,opt,name=blkio_stats" json:"blkio_stats,omitempty"` |
|
| 724 |
- HugetlbStats map[string]*HugetlbStats `protobuf:"bytes,4,rep,name=hugetlb_stats" json:"hugetlb_stats,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` |
|
| 725 |
- PidsStats *PidsStats `protobuf:"bytes,5,opt,name=pids_stats" json:"pids_stats,omitempty"` |
|
| 721 |
+ CpuStats *CpuStats `protobuf:"bytes,1,opt,name=cpu_stats,json=cpuStats" json:"cpu_stats,omitempty"` |
|
| 722 |
+ MemoryStats *MemoryStats `protobuf:"bytes,2,opt,name=memory_stats,json=memoryStats" json:"memory_stats,omitempty"` |
|
| 723 |
+ BlkioStats *BlkioStats `protobuf:"bytes,3,opt,name=blkio_stats,json=blkioStats" json:"blkio_stats,omitempty"` |
|
| 724 |
+ HugetlbStats map[string]*HugetlbStats `protobuf:"bytes,4,rep,name=hugetlb_stats,json=hugetlbStats" json:"hugetlb_stats,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` |
|
| 725 |
+ PidsStats *PidsStats `protobuf:"bytes,5,opt,name=pids_stats,json=pidsStats" json:"pids_stats,omitempty"` |
|
| 726 | 726 |
} |
| 727 | 727 |
|
| 728 | 728 |
func (m *CgroupStats) Reset() { *m = CgroupStats{} }
|
| ... | ... |
@@ -766,8 +773,8 @@ func (m *CgroupStats) GetPidsStats() *PidsStats {
|
| 766 | 766 |
} |
| 767 | 767 |
|
| 768 | 768 |
type StatsResponse struct {
|
| 769 |
- NetworkStats []*NetworkStats `protobuf:"bytes,1,rep,name=network_stats" json:"network_stats,omitempty"` |
|
| 770 |
- CgroupStats *CgroupStats `protobuf:"bytes,2,opt,name=cgroup_stats" json:"cgroup_stats,omitempty"` |
|
| 769 |
+ NetworkStats []*NetworkStats `protobuf:"bytes,1,rep,name=network_stats,json=networkStats" json:"network_stats,omitempty"` |
|
| 770 |
+ CgroupStats *CgroupStats `protobuf:"bytes,2,opt,name=cgroup_stats,json=cgroupStats" json:"cgroup_stats,omitempty"` |
|
| 771 | 771 |
Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp" json:"timestamp,omitempty"` |
| 772 | 772 |
} |
| 773 | 773 |
|
| ... | ... |
@@ -849,6 +856,10 @@ func init() {
|
| 849 | 849 |
var _ context.Context |
| 850 | 850 |
var _ grpc.ClientConn |
| 851 | 851 |
|
| 852 |
+// This is a compile-time assertion to ensure that this generated file |
|
| 853 |
+// is compatible with the grpc package it is being compiled against. |
|
| 854 |
+const _ = grpc.SupportPackageIsVersion2 |
|
| 855 |
+ |
|
| 852 | 856 |
// Client API for API service |
| 853 | 857 |
|
| 854 | 858 |
type APIClient interface {
|
| ... | ... |
@@ -1026,124 +1037,184 @@ func RegisterAPIServer(s *grpc.Server, srv APIServer) {
|
| 1026 | 1026 |
s.RegisterService(&_API_serviceDesc, srv) |
| 1027 | 1027 |
} |
| 1028 | 1028 |
|
| 1029 |
-func _API_GetServerVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1029 |
+func _API_GetServerVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1030 | 1030 |
in := new(GetServerVersionRequest) |
| 1031 | 1031 |
if err := dec(in); err != nil {
|
| 1032 | 1032 |
return nil, err |
| 1033 | 1033 |
} |
| 1034 |
- out, err := srv.(APIServer).GetServerVersion(ctx, in) |
|
| 1035 |
- if err != nil {
|
|
| 1036 |
- return nil, err |
|
| 1034 |
+ if interceptor == nil {
|
|
| 1035 |
+ return srv.(APIServer).GetServerVersion(ctx, in) |
|
| 1037 | 1036 |
} |
| 1038 |
- return out, nil |
|
| 1037 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1038 |
+ Server: srv, |
|
| 1039 |
+ FullMethod: "/types.API/GetServerVersion", |
|
| 1040 |
+ } |
|
| 1041 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1042 |
+ return srv.(APIServer).GetServerVersion(ctx, req.(*GetServerVersionRequest)) |
|
| 1043 |
+ } |
|
| 1044 |
+ return interceptor(ctx, in, info, handler) |
|
| 1039 | 1045 |
} |
| 1040 | 1046 |
|
| 1041 |
-func _API_CreateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1047 |
+func _API_CreateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1042 | 1048 |
in := new(CreateContainerRequest) |
| 1043 | 1049 |
if err := dec(in); err != nil {
|
| 1044 | 1050 |
return nil, err |
| 1045 | 1051 |
} |
| 1046 |
- out, err := srv.(APIServer).CreateContainer(ctx, in) |
|
| 1047 |
- if err != nil {
|
|
| 1048 |
- return nil, err |
|
| 1052 |
+ if interceptor == nil {
|
|
| 1053 |
+ return srv.(APIServer).CreateContainer(ctx, in) |
|
| 1049 | 1054 |
} |
| 1050 |
- return out, nil |
|
| 1055 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1056 |
+ Server: srv, |
|
| 1057 |
+ FullMethod: "/types.API/CreateContainer", |
|
| 1058 |
+ } |
|
| 1059 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1060 |
+ return srv.(APIServer).CreateContainer(ctx, req.(*CreateContainerRequest)) |
|
| 1061 |
+ } |
|
| 1062 |
+ return interceptor(ctx, in, info, handler) |
|
| 1051 | 1063 |
} |
| 1052 | 1064 |
|
| 1053 |
-func _API_UpdateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1065 |
+func _API_UpdateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1054 | 1066 |
in := new(UpdateContainerRequest) |
| 1055 | 1067 |
if err := dec(in); err != nil {
|
| 1056 | 1068 |
return nil, err |
| 1057 | 1069 |
} |
| 1058 |
- out, err := srv.(APIServer).UpdateContainer(ctx, in) |
|
| 1059 |
- if err != nil {
|
|
| 1060 |
- return nil, err |
|
| 1070 |
+ if interceptor == nil {
|
|
| 1071 |
+ return srv.(APIServer).UpdateContainer(ctx, in) |
|
| 1061 | 1072 |
} |
| 1062 |
- return out, nil |
|
| 1073 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1074 |
+ Server: srv, |
|
| 1075 |
+ FullMethod: "/types.API/UpdateContainer", |
|
| 1076 |
+ } |
|
| 1077 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1078 |
+ return srv.(APIServer).UpdateContainer(ctx, req.(*UpdateContainerRequest)) |
|
| 1079 |
+ } |
|
| 1080 |
+ return interceptor(ctx, in, info, handler) |
|
| 1063 | 1081 |
} |
| 1064 | 1082 |
|
| 1065 |
-func _API_Signal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1083 |
+func _API_Signal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1066 | 1084 |
in := new(SignalRequest) |
| 1067 | 1085 |
if err := dec(in); err != nil {
|
| 1068 | 1086 |
return nil, err |
| 1069 | 1087 |
} |
| 1070 |
- out, err := srv.(APIServer).Signal(ctx, in) |
|
| 1071 |
- if err != nil {
|
|
| 1072 |
- return nil, err |
|
| 1088 |
+ if interceptor == nil {
|
|
| 1089 |
+ return srv.(APIServer).Signal(ctx, in) |
|
| 1073 | 1090 |
} |
| 1074 |
- return out, nil |
|
| 1091 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1092 |
+ Server: srv, |
|
| 1093 |
+ FullMethod: "/types.API/Signal", |
|
| 1094 |
+ } |
|
| 1095 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1096 |
+ return srv.(APIServer).Signal(ctx, req.(*SignalRequest)) |
|
| 1097 |
+ } |
|
| 1098 |
+ return interceptor(ctx, in, info, handler) |
|
| 1075 | 1099 |
} |
| 1076 | 1100 |
|
| 1077 |
-func _API_UpdateProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1101 |
+func _API_UpdateProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1078 | 1102 |
in := new(UpdateProcessRequest) |
| 1079 | 1103 |
if err := dec(in); err != nil {
|
| 1080 | 1104 |
return nil, err |
| 1081 | 1105 |
} |
| 1082 |
- out, err := srv.(APIServer).UpdateProcess(ctx, in) |
|
| 1083 |
- if err != nil {
|
|
| 1084 |
- return nil, err |
|
| 1106 |
+ if interceptor == nil {
|
|
| 1107 |
+ return srv.(APIServer).UpdateProcess(ctx, in) |
|
| 1085 | 1108 |
} |
| 1086 |
- return out, nil |
|
| 1109 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1110 |
+ Server: srv, |
|
| 1111 |
+ FullMethod: "/types.API/UpdateProcess", |
|
| 1112 |
+ } |
|
| 1113 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1114 |
+ return srv.(APIServer).UpdateProcess(ctx, req.(*UpdateProcessRequest)) |
|
| 1115 |
+ } |
|
| 1116 |
+ return interceptor(ctx, in, info, handler) |
|
| 1087 | 1117 |
} |
| 1088 | 1118 |
|
| 1089 |
-func _API_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1119 |
+func _API_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1090 | 1120 |
in := new(AddProcessRequest) |
| 1091 | 1121 |
if err := dec(in); err != nil {
|
| 1092 | 1122 |
return nil, err |
| 1093 | 1123 |
} |
| 1094 |
- out, err := srv.(APIServer).AddProcess(ctx, in) |
|
| 1095 |
- if err != nil {
|
|
| 1096 |
- return nil, err |
|
| 1124 |
+ if interceptor == nil {
|
|
| 1125 |
+ return srv.(APIServer).AddProcess(ctx, in) |
|
| 1097 | 1126 |
} |
| 1098 |
- return out, nil |
|
| 1127 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1128 |
+ Server: srv, |
|
| 1129 |
+ FullMethod: "/types.API/AddProcess", |
|
| 1130 |
+ } |
|
| 1131 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1132 |
+ return srv.(APIServer).AddProcess(ctx, req.(*AddProcessRequest)) |
|
| 1133 |
+ } |
|
| 1134 |
+ return interceptor(ctx, in, info, handler) |
|
| 1099 | 1135 |
} |
| 1100 | 1136 |
|
| 1101 |
-func _API_CreateCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1137 |
+func _API_CreateCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1102 | 1138 |
in := new(CreateCheckpointRequest) |
| 1103 | 1139 |
if err := dec(in); err != nil {
|
| 1104 | 1140 |
return nil, err |
| 1105 | 1141 |
} |
| 1106 |
- out, err := srv.(APIServer).CreateCheckpoint(ctx, in) |
|
| 1107 |
- if err != nil {
|
|
| 1108 |
- return nil, err |
|
| 1142 |
+ if interceptor == nil {
|
|
| 1143 |
+ return srv.(APIServer).CreateCheckpoint(ctx, in) |
|
| 1109 | 1144 |
} |
| 1110 |
- return out, nil |
|
| 1145 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1146 |
+ Server: srv, |
|
| 1147 |
+ FullMethod: "/types.API/CreateCheckpoint", |
|
| 1148 |
+ } |
|
| 1149 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1150 |
+ return srv.(APIServer).CreateCheckpoint(ctx, req.(*CreateCheckpointRequest)) |
|
| 1151 |
+ } |
|
| 1152 |
+ return interceptor(ctx, in, info, handler) |
|
| 1111 | 1153 |
} |
| 1112 | 1154 |
|
| 1113 |
-func _API_DeleteCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1155 |
+func _API_DeleteCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1114 | 1156 |
in := new(DeleteCheckpointRequest) |
| 1115 | 1157 |
if err := dec(in); err != nil {
|
| 1116 | 1158 |
return nil, err |
| 1117 | 1159 |
} |
| 1118 |
- out, err := srv.(APIServer).DeleteCheckpoint(ctx, in) |
|
| 1119 |
- if err != nil {
|
|
| 1120 |
- return nil, err |
|
| 1160 |
+ if interceptor == nil {
|
|
| 1161 |
+ return srv.(APIServer).DeleteCheckpoint(ctx, in) |
|
| 1121 | 1162 |
} |
| 1122 |
- return out, nil |
|
| 1163 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1164 |
+ Server: srv, |
|
| 1165 |
+ FullMethod: "/types.API/DeleteCheckpoint", |
|
| 1166 |
+ } |
|
| 1167 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1168 |
+ return srv.(APIServer).DeleteCheckpoint(ctx, req.(*DeleteCheckpointRequest)) |
|
| 1169 |
+ } |
|
| 1170 |
+ return interceptor(ctx, in, info, handler) |
|
| 1123 | 1171 |
} |
| 1124 | 1172 |
|
| 1125 |
-func _API_ListCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1173 |
+func _API_ListCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1126 | 1174 |
in := new(ListCheckpointRequest) |
| 1127 | 1175 |
if err := dec(in); err != nil {
|
| 1128 | 1176 |
return nil, err |
| 1129 | 1177 |
} |
| 1130 |
- out, err := srv.(APIServer).ListCheckpoint(ctx, in) |
|
| 1131 |
- if err != nil {
|
|
| 1132 |
- return nil, err |
|
| 1178 |
+ if interceptor == nil {
|
|
| 1179 |
+ return srv.(APIServer).ListCheckpoint(ctx, in) |
|
| 1133 | 1180 |
} |
| 1134 |
- return out, nil |
|
| 1181 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1182 |
+ Server: srv, |
|
| 1183 |
+ FullMethod: "/types.API/ListCheckpoint", |
|
| 1184 |
+ } |
|
| 1185 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1186 |
+ return srv.(APIServer).ListCheckpoint(ctx, req.(*ListCheckpointRequest)) |
|
| 1187 |
+ } |
|
| 1188 |
+ return interceptor(ctx, in, info, handler) |
|
| 1135 | 1189 |
} |
| 1136 | 1190 |
|
| 1137 |
-func _API_State_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1191 |
+func _API_State_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1138 | 1192 |
in := new(StateRequest) |
| 1139 | 1193 |
if err := dec(in); err != nil {
|
| 1140 | 1194 |
return nil, err |
| 1141 | 1195 |
} |
| 1142 |
- out, err := srv.(APIServer).State(ctx, in) |
|
| 1143 |
- if err != nil {
|
|
| 1144 |
- return nil, err |
|
| 1196 |
+ if interceptor == nil {
|
|
| 1197 |
+ return srv.(APIServer).State(ctx, in) |
|
| 1145 | 1198 |
} |
| 1146 |
- return out, nil |
|
| 1199 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1200 |
+ Server: srv, |
|
| 1201 |
+ FullMethod: "/types.API/State", |
|
| 1202 |
+ } |
|
| 1203 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1204 |
+ return srv.(APIServer).State(ctx, req.(*StateRequest)) |
|
| 1205 |
+ } |
|
| 1206 |
+ return interceptor(ctx, in, info, handler) |
|
| 1147 | 1207 |
} |
| 1148 | 1208 |
|
| 1149 | 1209 |
func _API_Events_Handler(srv interface{}, stream grpc.ServerStream) error {
|
| ... | ... |
@@ -1167,16 +1238,22 @@ func (x *aPIEventsServer) Send(m *Event) error {
|
| 1167 | 1167 |
return x.ServerStream.SendMsg(m) |
| 1168 | 1168 |
} |
| 1169 | 1169 |
|
| 1170 |
-func _API_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
|
|
| 1170 |
+func _API_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
| 1171 | 1171 |
in := new(StatsRequest) |
| 1172 | 1172 |
if err := dec(in); err != nil {
|
| 1173 | 1173 |
return nil, err |
| 1174 | 1174 |
} |
| 1175 |
- out, err := srv.(APIServer).Stats(ctx, in) |
|
| 1176 |
- if err != nil {
|
|
| 1177 |
- return nil, err |
|
| 1175 |
+ if interceptor == nil {
|
|
| 1176 |
+ return srv.(APIServer).Stats(ctx, in) |
|
| 1178 | 1177 |
} |
| 1179 |
- return out, nil |
|
| 1178 |
+ info := &grpc.UnaryServerInfo{
|
|
| 1179 |
+ Server: srv, |
|
| 1180 |
+ FullMethod: "/types.API/Stats", |
|
| 1181 |
+ } |
|
| 1182 |
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
| 1183 |
+ return srv.(APIServer).Stats(ctx, req.(*StatsRequest)) |
|
| 1184 |
+ } |
|
| 1185 |
+ return interceptor(ctx, in, info, handler) |
|
| 1180 | 1186 |
} |
| 1181 | 1187 |
|
| 1182 | 1188 |
var _API_serviceDesc = grpc.ServiceDesc{
|
| ... | ... |
@@ -1238,148 +1315,152 @@ var _API_serviceDesc = grpc.ServiceDesc{
|
| 1238 | 1238 |
} |
| 1239 | 1239 |
|
| 1240 | 1240 |
var fileDescriptor0 = []byte{
|
| 1241 |
- // 2285 bytes of a gzipped FileDescriptorProto |
|
| 1242 |
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xec, 0x19, 0xcb, 0x72, 0x1c, 0x49, |
|
| 1243 |
- 0xd1, 0xf3, 0x94, 0x26, 0xe7, 0x21, 0xa9, 0xfd, 0xd0, 0x78, 0x76, 0xed, 0x35, 0x1d, 0xc0, 0x1a, |
|
| 1244 |
- 0x58, 0x84, 0x91, 0x77, 0x03, 0x07, 0x04, 0x44, 0xac, 0x65, 0xb3, 0x98, 0xb5, 0x16, 0xb9, 0x25, |
|
| 1245 |
- 0xb1, 0x17, 0x22, 0x26, 0x5a, 0x33, 0xe5, 0x99, 0x46, 0x33, 0xdd, 0xbd, 0xdd, 0x35, 0xd2, 0xe8, |
|
| 1246 |
- 0xc2, 0x11, 0x6e, 0xfc, 0x00, 0x11, 0x5c, 0xb8, 0x71, 0xe7, 0xc0, 0x17, 0xf0, 0x27, 0xc4, 0x5e, |
|
| 1247 |
- 0xb8, 0x73, 0x24, 0xab, 0x32, 0xbb, 0xba, 0x7a, 0x1e, 0xd2, 0x72, 0x20, 0xb8, 0xec, 0x65, 0xa2, |
|
| 1248 |
- 0x32, 0x2b, 0x2b, 0x33, 0x2b, 0xdf, 0x5d, 0x03, 0x0d, 0x3f, 0x0e, 0xf6, 0xe2, 0x24, 0x92, 0x91, |
|
| 1249 |
- 0x53, 0x93, 0x57, 0xb1, 0x48, 0xdd, 0xfb, 0xb0, 0xfb, 0x89, 0x90, 0xc7, 0x22, 0xb9, 0x10, 0xc9, |
|
| 1250 |
- 0xaf, 0x45, 0x92, 0x06, 0x51, 0xe8, 0x89, 0x2f, 0x66, 0x22, 0x95, 0xee, 0x1c, 0xba, 0xcb, 0x5b, |
|
| 1251 |
- 0x69, 0x1c, 0x85, 0xa9, 0x70, 0xee, 0x40, 0x6d, 0xea, 0xff, 0x36, 0x4a, 0xba, 0xa5, 0x47, 0xa5, |
|
| 1252 |
- 0xc7, 0x6d, 0x8f, 0x00, 0x8d, 0x0d, 0x42, 0xc4, 0x96, 0x19, 0xab, 0x00, 0x85, 0x8d, 0x7d, 0x39, |
|
| 1253 |
- 0x18, 0x77, 0x2b, 0x84, 0xd5, 0x80, 0xd3, 0x83, 0xcd, 0x44, 0x5c, 0x04, 0x8a, 0x6b, 0xb7, 0x8a, |
|
| 1254 |
- 0x1b, 0x0d, 0xcf, 0xc0, 0xee, 0xef, 0x4b, 0x70, 0xe7, 0x34, 0x1e, 0xfa, 0x52, 0x1c, 0x25, 0xd1, |
|
| 1255 |
- 0x40, 0xa4, 0x29, 0xab, 0xe4, 0x74, 0xa0, 0x1c, 0x0c, 0xb5, 0xcc, 0x86, 0x87, 0x2b, 0x67, 0x1b, |
|
| 1256 |
- 0x2a, 0x31, 0x22, 0xca, 0x1a, 0xa1, 0x96, 0xce, 0x43, 0x80, 0xc1, 0x24, 0x4a, 0xc5, 0xb1, 0x1c, |
|
| 1257 |
- 0x06, 0xa1, 0x96, 0xb8, 0xe9, 0x59, 0x18, 0xa5, 0xcc, 0x65, 0x30, 0x94, 0x63, 0x2d, 0x13, 0x95, |
|
| 1258 |
- 0xd1, 0x80, 0x73, 0x0f, 0xea, 0x63, 0x11, 0x8c, 0xc6, 0xb2, 0x5b, 0xd3, 0x68, 0x86, 0xdc, 0x5d, |
|
| 1259 |
- 0xb8, 0xbb, 0xa0, 0x07, 0xdd, 0xdf, 0xfd, 0xb2, 0x04, 0xf7, 0x0e, 0x12, 0x81, 0x3b, 0x07, 0x51, |
|
| 1260 |
- 0x28, 0xfd, 0x20, 0x14, 0xc9, 0x3a, 0x1d, 0x51, 0xa3, 0xb3, 0x59, 0x38, 0x9c, 0x88, 0x23, 0x1f, |
|
| 1261 |
- 0xc5, 0x92, 0xaa, 0x16, 0x46, 0x6b, 0x3c, 0x16, 0x83, 0xf3, 0x38, 0x0a, 0x42, 0xa9, 0x35, 0xc6, |
|
| 1262 |
- 0xfd, 0x1c, 0xa3, 0x34, 0x4e, 0xf5, 0x65, 0xc8, 0x4a, 0x04, 0x28, 0x8d, 0x71, 0x11, 0xcd, 0x48, |
|
| 1263 |
- 0xe3, 0x86, 0xc7, 0x10, 0xe3, 0x45, 0x92, 0x74, 0xeb, 0x06, 0x8f, 0x90, 0xc2, 0x4f, 0xfc, 0x33, |
|
| 1264 |
- 0x31, 0x49, 0xbb, 0x1b, 0x8f, 0x2a, 0x0a, 0x4f, 0x90, 0xf3, 0x08, 0x9a, 0x61, 0x74, 0x14, 0x5c, |
|
| 1265 |
- 0x44, 0xd2, 0x8b, 0x22, 0xd9, 0xdd, 0xd4, 0x06, 0xb3, 0x51, 0xee, 0x2b, 0xd8, 0x5d, 0xba, 0x29, |
|
| 1266 |
- 0x47, 0xc1, 0x1e, 0x34, 0x06, 0x19, 0x52, 0xdf, 0xb8, 0xb9, 0xbf, 0xbd, 0xa7, 0xe3, 0x6a, 0x2f, |
|
| 1267 |
- 0x27, 0xce, 0x49, 0x90, 0x55, 0xfb, 0x38, 0x18, 0x85, 0xfe, 0xe4, 0xab, 0xfb, 0x53, 0xdd, 0x47, |
|
| 1268 |
- 0x1f, 0xe1, 0xe8, 0x61, 0xc8, 0xdd, 0x86, 0x4e, 0xc6, 0x8a, 0x5d, 0xf2, 0xb7, 0x0a, 0xec, 0x7c, |
|
| 1269 |
- 0x3c, 0x1c, 0xde, 0x10, 0x31, 0x18, 0x76, 0x52, 0x24, 0x18, 0x98, 0xc8, 0xb1, 0xac, 0x2f, 0x6b, |
|
| 1270 |
- 0x60, 0xe7, 0x3d, 0xa8, 0xce, 0x52, 0xbc, 0x49, 0x45, 0xdf, 0xa4, 0xc9, 0x37, 0x39, 0x45, 0x94, |
|
| 1271 |
- 0xa7, 0x37, 0x1c, 0x07, 0xaa, 0x7e, 0x32, 0x4a, 0xd1, 0x13, 0xca, 0x84, 0x7a, 0xad, 0x54, 0x16, |
|
| 1272 |
- 0xe1, 0x05, 0x7a, 0x41, 0xa1, 0xd4, 0x52, 0x61, 0x06, 0x97, 0x43, 0xb6, 0xbf, 0x5a, 0x66, 0xd7, |
|
| 1273 |
- 0xda, 0xc8, 0xaf, 0x65, 0x9c, 0xba, 0xb9, 0xda, 0xa9, 0x8d, 0x35, 0x4e, 0x85, 0x82, 0x53, 0x5d, |
|
| 1274 |
- 0x68, 0x0d, 0xfc, 0xd8, 0x3f, 0x0b, 0x26, 0x81, 0x0c, 0x44, 0xda, 0x6d, 0x6a, 0x25, 0x0a, 0x38, |
|
| 1275 |
- 0xe7, 0x31, 0x6c, 0xf9, 0x71, 0xec, 0x27, 0xd3, 0x28, 0x41, 0xd3, 0xbc, 0x0d, 0x26, 0xa2, 0xdb, |
|
| 1276 |
- 0xd2, 0x4c, 0x16, 0xd1, 0x8a, 0x5b, 0x2a, 0x26, 0x41, 0x38, 0x9b, 0xbf, 0x56, 0xb1, 0xd1, 0x6d, |
|
| 1277 |
- 0x6b, 0xb2, 0x02, 0x4e, 0x71, 0x0b, 0xa3, 0xcf, 0xc4, 0xe5, 0x51, 0x12, 0x5c, 0xe0, 0x99, 0x11, |
|
| 1278 |
- 0x0a, 0xed, 0x68, 0x2b, 0x2e, 0xa2, 0x9d, 0xf7, 0x61, 0x23, 0x99, 0x04, 0xd3, 0x40, 0xa6, 0xdd, |
|
| 1279 |
- 0x2d, 0x54, 0xab, 0xb9, 0xdf, 0x66, 0x7b, 0x7a, 0x1a, 0xeb, 0x65, 0xbb, 0xee, 0x0b, 0xa8, 0x13, |
|
| 1280 |
- 0x4a, 0x99, 0x57, 0x91, 0xb0, 0xb7, 0xf4, 0x5a, 0xe1, 0xd2, 0xe8, 0xad, 0xd4, 0xbe, 0xaa, 0x7a, |
|
| 1281 |
- 0x7a, 0xad, 0x70, 0x63, 0x3f, 0x19, 0x6a, 0x3f, 0x21, 0x4e, 0xad, 0x5d, 0x0f, 0xaa, 0xca, 0x51, |
|
| 1282 |
- 0xca, 0xd4, 0x33, 0x76, 0x78, 0xdb, 0x53, 0x4b, 0x85, 0x19, 0x71, 0x4c, 0x21, 0x06, 0x97, 0xce, |
|
| 1283 |
- 0xb7, 0xa1, 0xe3, 0x0f, 0x87, 0x68, 0x9e, 0x08, 0xbd, 0xfe, 0x49, 0x30, 0x4c, 0x91, 0x53, 0x05, |
|
| 1284 |
- 0x37, 0x17, 0xb0, 0xee, 0x1d, 0x70, 0xec, 0x80, 0xe2, 0x38, 0xfb, 0x8d, 0xc9, 0x07, 0x93, 0xa3, |
|
| 1285 |
- 0xeb, 0x82, 0xed, 0x87, 0x85, 0xd4, 0x2e, 0xeb, 0xb0, 0xda, 0xc9, 0x12, 0x24, 0x3f, 0x6d, 0x11, |
|
| 1286 |
- 0xb9, 0x3d, 0xe8, 0x2e, 0x73, 0x67, 0xc9, 0x3f, 0x85, 0xdd, 0x17, 0x62, 0x22, 0xbe, 0x8a, 0x64, |
|
| 1287 |
- 0x34, 0x51, 0xe8, 0x4f, 0x05, 0x67, 0x92, 0x5e, 0x2b, 0xd6, 0xcb, 0xc7, 0x99, 0xf5, 0xfb, 0x70, |
|
| 1288 |
- 0xf7, 0x75, 0x90, 0xca, 0x1b, 0x19, 0xbb, 0xbf, 0x03, 0xc8, 0x89, 0x8c, 0x98, 0x52, 0x2e, 0x46, |
|
| 1289 |
- 0xe1, 0xc4, 0x3c, 0x90, 0x9c, 0x5d, 0x7a, 0xad, 0x7c, 0x20, 0x07, 0x31, 0x97, 0x63, 0xb5, 0x54, |
|
| 1290 |
- 0x75, 0x67, 0x16, 0x06, 0xf3, 0xe3, 0x68, 0x70, 0x2e, 0x64, 0xaa, 0x6b, 0x1b, 0xd6, 0x1d, 0x0b, |
|
| 1291 |
- 0xa5, 0x53, 0x64, 0x2c, 0x26, 0x13, 0x5d, 0xe0, 0x36, 0x3d, 0x02, 0xdc, 0x43, 0xb8, 0xb7, 0xa8, |
|
| 1292 |
- 0x28, 0x17, 0xa3, 0xa7, 0xd0, 0xcc, 0xed, 0x98, 0xa2, 0x4a, 0x95, 0xd5, 0xd6, 0xb6, 0xa9, 0xdc, |
|
| 1293 |
- 0x87, 0xd0, 0x3a, 0x96, 0x68, 0xed, 0x75, 0xd7, 0x7d, 0x0c, 0x1d, 0x53, 0xc9, 0x34, 0x21, 0xe5, |
|
| 1294 |
- 0xa2, 0x2f, 0x67, 0x29, 0x53, 0x31, 0xe4, 0xfe, 0xbd, 0x02, 0x1b, 0x1c, 0x2a, 0x59, 0xbe, 0x97, |
|
| 1295 |
- 0xf2, 0x7c, 0xff, 0xbf, 0x94, 0x9d, 0x77, 0xa1, 0x91, 0x5e, 0xa5, 0x52, 0x4c, 0x8f, 0xb8, 0xf8, |
|
| 1296 |
- 0xb4, 0xbd, 0x1c, 0xf1, 0x75, 0x09, 0xca, 0x4b, 0xd0, 0x3f, 0x4a, 0xd0, 0x30, 0x6e, 0xfe, 0xaf, |
|
| 1297 |
- 0x1b, 0xf8, 0x07, 0xd0, 0x88, 0xc9, 0xf1, 0x82, 0x2a, 0x49, 0x73, 0xbf, 0xc3, 0x82, 0xb2, 0xda, |
|
| 1298 |
- 0x91, 0x13, 0x58, 0xf1, 0x53, 0xb5, 0xe3, 0xc7, 0x6a, 0xd0, 0xb5, 0x42, 0x83, 0x46, 0xe7, 0xc7, |
|
| 1299 |
- 0xaa, 0x44, 0xd5, 0x75, 0x89, 0xd2, 0x6b, 0xa7, 0x8b, 0x17, 0x9b, 0x85, 0x32, 0xc0, 0xcc, 0xa3, |
|
| 1300 |
- 0x9e, 0x92, 0x81, 0xee, 0x47, 0xb0, 0x71, 0xe8, 0x0f, 0xc6, 0x78, 0x0f, 0x75, 0x70, 0x10, 0x73, |
|
| 1301 |
- 0x98, 0xe2, 0x41, 0xb5, 0x56, 0x42, 0xa6, 0x02, 0xed, 0x7d, 0xc5, 0xf5, 0x94, 0x21, 0xf7, 0x1c, |
|
| 1302 |
- 0x1b, 0x33, 0xa5, 0x01, 0x27, 0xd3, 0x13, 0xac, 0x5c, 0x99, 0x41, 0xb2, 0x5c, 0x5a, 0x6e, 0xed, |
|
| 1303 |
- 0x16, 0x0d, 0xba, 0x65, 0x63, 0x4a, 0x92, 0xb9, 0xd0, 0x65, 0x36, 0x60, 0x7d, 0xbc, 0x6c, 0xdb, |
|
| 1304 |
- 0xfd, 0x03, 0xce, 0x4e, 0x34, 0x55, 0xdd, 0x38, 0x3b, 0xad, 0x9e, 0x07, 0xc8, 0x7c, 0x95, 0x82, |
|
| 1305 |
- 0xf9, 0x9e, 0x42, 0x23, 0x11, 0x69, 0x34, 0x4b, 0xd0, 0xcc, 0xda, 0xb2, 0xcd, 0xfd, 0xbb, 0x59, |
|
| 1306 |
- 0x26, 0x69, 0x59, 0x1e, 0xef, 0x7a, 0x39, 0x9d, 0xfb, 0x65, 0x19, 0x3a, 0xc5, 0x5d, 0x55, 0x97, |
|
| 1307 |
- 0xce, 0x26, 0xe7, 0x41, 0xf4, 0x39, 0x8d, 0x83, 0x64, 0x3c, 0x1b, 0xa5, 0xb2, 0x0a, 0x6d, 0x79, |
|
| 1308 |
- 0x8c, 0x5d, 0x07, 0x25, 0x51, 0x57, 0xc9, 0x11, 0xbc, 0x7b, 0x24, 0x92, 0x20, 0x1a, 0xf2, 0xc8, |
|
| 1309 |
- 0x92, 0x23, 0x54, 0x19, 0x40, 0xe0, 0xcd, 0x2c, 0x92, 0x3e, 0x0f, 0xa0, 0x06, 0xd6, 0x73, 0x20, |
|
| 1310 |
- 0xfa, 0x48, 0xc8, 0x03, 0xe5, 0xb5, 0x1a, 0xcf, 0x81, 0x06, 0x93, 0xef, 0x1f, 0x8a, 0x69, 0xca, |
|
| 1311 |
- 0x69, 0x6e, 0x61, 0x94, 0xe6, 0xe4, 0xcd, 0xd7, 0x2a, 0xa8, 0x39, 0xdf, 0x6d, 0x94, 0xe2, 0x40, |
|
| 1312 |
- 0xe0, 0xf1, 0xa5, 0x1f, 0xeb, 0xb4, 0x6f, 0x7b, 0x16, 0x06, 0x03, 0x79, 0x87, 0x20, 0xb4, 0x06, |
|
| 1313 |
- 0x4e, 0xfd, 0xbe, 0x6a, 0x85, 0xba, 0x0c, 0xb4, 0xbd, 0xe5, 0x0d, 0x45, 0x7d, 0x2e, 0x92, 0x50, |
|
| 1314 |
- 0x4c, 0x0e, 0x2d, 0xa9, 0x40, 0xd4, 0x4b, 0x1b, 0xea, 0x3b, 0x63, 0xc9, 0xe7, 0xdc, 0x7b, 0xbe, |
|
| 1315 |
- 0x0f, 0xed, 0x97, 0x17, 0x02, 0xab, 0x71, 0x16, 0x05, 0x68, 0x43, 0x15, 0xcc, 0xe8, 0xd9, 0x69, |
|
| 1316 |
- 0xac, 0x3d, 0x50, 0xf5, 0x72, 0x84, 0x9b, 0x42, 0x4d, 0x93, 0xaf, 0x1c, 0x17, 0x28, 0x80, 0xca, |
|
| 1317 |
- 0x26, 0x80, 0x8a, 0xe1, 0xd2, 0x36, 0xe1, 0xc2, 0x81, 0x55, 0xcd, 0x03, 0xab, 0x20, 0xb4, 0xb6, |
|
| 1318 |
- 0x28, 0xf4, 0x8f, 0x65, 0x68, 0x7d, 0x26, 0xe4, 0x65, 0x94, 0x9c, 0xab, 0x44, 0x49, 0x57, 0x76, |
|
| 1319 |
- 0xbe, 0xfb, 0xf8, 0x49, 0x33, 0xef, 0x9f, 0x5d, 0x49, 0x0e, 0x8c, 0x2a, 0xe6, 0xe5, 0xfc, 0xb9, |
|
| 1320 |
- 0x02, 0x9d, 0x07, 0x00, 0xb8, 0x75, 0xe4, 0x53, 0xb7, 0xa3, 0xc1, 0xa5, 0x91, 0xcc, 0x19, 0xe1, |
|
| 1321 |
- 0xbc, 0x03, 0x0d, 0x6f, 0xde, 0xc7, 0x7a, 0x1a, 0x25, 0x14, 0xbd, 0x55, 0xfc, 0x1a, 0x9a, 0xbf, |
|
| 1322 |
- 0xd4, 0xb0, 0x3a, 0x8b, 0x9b, 0xc3, 0x24, 0x8a, 0x63, 0x31, 0xcc, 0x54, 0x4b, 0xe6, 0x2f, 0x08, |
|
| 1323 |
- 0xa1, 0xa4, 0x9e, 0x64, 0x52, 0xeb, 0x24, 0x55, 0xe6, 0x52, 0x71, 0x2b, 0x66, 0xa9, 0x1b, 0x7c, |
|
| 1324 |
- 0x29, 0x5b, 0xea, 0x89, 0x91, 0xba, 0x49, 0x52, 0xa5, 0x25, 0xf5, 0x24, 0x97, 0xda, 0xc8, 0xce, |
|
| 1325 |
- 0xb2, 0x54, 0xf7, 0xaf, 0x25, 0xd8, 0xc4, 0xb0, 0x3c, 0x4d, 0xfd, 0x91, 0xc0, 0x0e, 0xd6, 0x94, |
|
| 1326 |
- 0x18, 0xc2, 0x93, 0xfe, 0x4c, 0x81, 0xec, 0x32, 0xd0, 0x28, 0x22, 0xf8, 0x06, 0xb4, 0x62, 0x91, |
|
| 1327 |
- 0x60, 0xb0, 0x32, 0x45, 0x19, 0x0b, 0x4a, 0xd5, 0x6b, 0x12, 0x8e, 0x48, 0xf6, 0xe0, 0xb6, 0xde, |
|
| 1328 |
- 0xeb, 0x07, 0x61, 0x9f, 0xc2, 0x67, 0x1a, 0x0d, 0x05, 0x9b, 0x6a, 0x47, 0x6f, 0xbd, 0x0a, 0x3f, |
|
| 1329 |
- 0x35, 0x1b, 0xce, 0x77, 0x61, 0xc7, 0xd0, 0xab, 0x2e, 0xa9, 0xa9, 0xc9, 0x74, 0x5b, 0x4c, 0x7d, |
|
| 1330 |
- 0xca, 0x68, 0x1c, 0x5a, 0x3a, 0x27, 0x63, 0xfc, 0xea, 0x95, 0xd8, 0x46, 0x46, 0x2f, 0x7c, 0x4c, |
|
| 1331 |
- 0x36, 0xac, 0xa0, 0xb1, 0x4e, 0xc9, 0x94, 0xb5, 0xcd, 0x40, 0xe7, 0x7b, 0xb0, 0x23, 0x89, 0x56, |
|
| 1332 |
- 0x0c, 0xfb, 0x19, 0x0d, 0x79, 0x73, 0xdb, 0x6c, 0x1c, 0x31, 0xf1, 0xb7, 0xa0, 0x93, 0x13, 0xeb, |
|
| 1333 |
- 0x7a, 0x4c, 0xfa, 0xb6, 0x0d, 0xf6, 0x44, 0x55, 0xe5, 0x3f, 0x91, 0xb1, 0x28, 0x72, 0x3e, 0xd0, |
|
| 1334 |
- 0x15, 0xc2, 0x32, 0x55, 0x73, 0x7f, 0x2b, 0xab, 0xac, 0x6c, 0x0c, 0x5d, 0x15, 0xc8, 0x2c, 0x3f, |
|
| 1335 |
- 0x83, 0x2d, 0x69, 0x54, 0xef, 0x63, 0x02, 0xf9, 0x5c, 0x5e, 0xb3, 0xea, 0x56, 0xbc, 0x98, 0xd7, |
|
| 1336 |
- 0x91, 0xc5, 0x8b, 0xa2, 0xe5, 0xa9, 0xe5, 0xb3, 0x40, 0xd2, 0xaf, 0x49, 0x38, 0x2d, 0xc2, 0xfd, |
|
| 1337 |
- 0x09, 0x34, 0x70, 0x1e, 0x48, 0x49, 0x3b, 0x34, 0xcc, 0x60, 0x96, 0x24, 0x98, 0x5f, 0x99, 0x61, |
|
| 1338 |
- 0x18, 0x54, 0xf3, 0x82, 0x6e, 0x97, 0x6c, 0x0c, 0x02, 0xdc, 0x08, 0x80, 0xd2, 0x5c, 0x4b, 0x43, |
|
| 1339 |
- 0x1a, 0x3b, 0x04, 0x08, 0x50, 0x71, 0x36, 0xf5, 0xe7, 0xc6, 0xf5, 0x3a, 0xce, 0x10, 0x41, 0x17, |
|
| 1340 |
- 0x44, 0x81, 0x6f, 0xfd, 0x60, 0x32, 0xe0, 0x6f, 0x5f, 0x14, 0xc8, 0x60, 0x2e, 0xb0, 0x6a, 0x0b, |
|
| 1341 |
- 0xfc, 0x4b, 0x19, 0x9a, 0x24, 0x91, 0x14, 0x46, 0xaa, 0x01, 0x36, 0x16, 0x23, 0x52, 0x03, 0xd8, |
|
| 1342 |
- 0xfa, 0x6b, 0xb9, 0xb8, 0x7c, 0x0c, 0xcc, 0x55, 0xcd, 0x74, 0xc3, 0x46, 0x97, 0x62, 0xed, 0xb3, |
|
| 1343 |
- 0xac, 0xb3, 0x92, 0xba, 0xa1, 0x88, 0x48, 0xe1, 0x0f, 0xa1, 0x45, 0xf1, 0xc9, 0x67, 0xaa, 0xeb, |
|
| 1344 |
- 0xce, 0x34, 0x89, 0x8c, 0x4e, 0x3d, 0x55, 0xd3, 0x16, 0xea, 0xab, 0xbb, 0x7b, 0x73, 0xff, 0x41, |
|
| 1345 |
- 0x81, 0x5c, 0xdf, 0x64, 0x4f, 0xff, 0xbe, 0x0c, 0x25, 0x96, 0x59, 0xa2, 0xed, 0x3d, 0x03, 0xc8, |
|
| 1346 |
- 0x91, 0xaa, 0x66, 0x9d, 0x8b, 0xab, 0x6c, 0xaa, 0xc4, 0xa5, 0xba, 0xfb, 0x85, 0x3f, 0x99, 0x65, |
|
| 1347 |
- 0x46, 0x25, 0xe0, 0xc7, 0xe5, 0x67, 0x25, 0x77, 0x00, 0x5b, 0xcf, 0x55, 0xcf, 0xb2, 0x8e, 0x17, |
|
| 1348 |
- 0x9e, 0x6c, 0xaa, 0x2b, 0x9f, 0x6c, 0xaa, 0xd9, 0x93, 0x0d, 0x96, 0xd1, 0x28, 0xe6, 0x0e, 0x8b, |
|
| 1349 |
- 0xab, 0x5c, 0x50, 0xd5, 0x12, 0xe4, 0xfe, 0xb3, 0x0a, 0x90, 0x4b, 0x71, 0x8e, 0xa1, 0x17, 0x44, |
|
| 1350 |
- 0x7d, 0xd5, 0x20, 0x82, 0x81, 0xa0, 0x82, 0xd4, 0x4f, 0x04, 0x86, 0x4f, 0x1a, 0x5c, 0x08, 0x9e, |
|
| 1351 |
- 0x21, 0xee, 0xf1, 0xbd, 0x17, 0x94, 0xf3, 0x76, 0x11, 0xa2, 0x83, 0xba, 0x72, 0x79, 0xd9, 0x31, |
|
| 1352 |
- 0xe7, 0x97, 0x70, 0x37, 0x67, 0x3a, 0xb4, 0xf8, 0x95, 0xaf, 0xe5, 0x77, 0xdb, 0xf0, 0x1b, 0xe6, |
|
| 1353 |
- 0xbc, 0x7e, 0x0e, 0x88, 0xee, 0x63, 0x8f, 0x99, 0x15, 0x38, 0x55, 0xae, 0xe5, 0xb4, 0x13, 0x44, |
|
| 1354 |
- 0x6f, 0xf4, 0x89, 0x9c, 0xcf, 0x1b, 0xb8, 0x6f, 0x5d, 0x54, 0xa5, 0xbd, 0xc5, 0xad, 0x7a, 0x2d, |
|
| 1355 |
- 0xb7, 0x7b, 0x46, 0x2f, 0x55, 0x18, 0x72, 0x96, 0x9f, 0x02, 0xee, 0xf4, 0x2f, 0xfd, 0x40, 0x2e, |
|
| 1356 |
- 0xf2, 0xab, 0xdd, 0x74, 0xcf, 0xcf, 0xf1, 0x50, 0x91, 0x19, 0xdd, 0x73, 0x2a, 0x92, 0x51, 0xe1, |
|
| 1357 |
- 0x9e, 0xf5, 0x9b, 0xee, 0x79, 0xa8, 0x4f, 0xe4, 0x7c, 0x9e, 0x03, 0x22, 0x17, 0xf5, 0xd9, 0xb8, |
|
| 1358 |
- 0x96, 0xcb, 0x56, 0x10, 0x15, 0x75, 0x39, 0x80, 0x9d, 0x54, 0x0c, 0x24, 0x76, 0x14, 0x8b, 0xc7, |
|
| 1359 |
- 0xe6, 0xb5, 0x3c, 0xb6, 0xf9, 0x80, 0x61, 0xe2, 0x7e, 0x01, 0xad, 0x5f, 0xcc, 0x46, 0x42, 0x4e, |
|
| 1360 |
- 0xce, 0x4c, 0xce, 0xff, 0xaf, 0xcb, 0xcc, 0xbf, 0xb1, 0xcc, 0x1c, 0x8c, 0x92, 0x68, 0x16, 0x17, |
|
| 1361 |
- 0xaa, 0x36, 0xe5, 0xf0, 0x52, 0xd5, 0xd6, 0x34, 0xba, 0x6a, 0x13, 0xf5, 0x47, 0xd0, 0xa2, 0x81, |
|
| 1362 |
- 0x89, 0x0f, 0x50, 0x15, 0x72, 0x96, 0x93, 0x3e, 0x1b, 0xd0, 0xe8, 0xd8, 0x3e, 0x0f, 0x9f, 0x7c, |
|
| 1363 |
- 0xaa, 0x58, 0x8d, 0x72, 0x33, 0xe1, 0xd7, 0x47, 0x9e, 0x75, 0xaf, 0xa0, 0x3d, 0x26, 0xdb, 0xf0, |
|
| 1364 |
- 0x29, 0x0a, 0xc0, 0x6f, 0x66, 0xca, 0xe5, 0x77, 0xd8, 0xb3, 0x6d, 0x48, 0xa6, 0x6e, 0x8d, 0x6d, |
|
| 1365 |
- 0xb3, 0xfe, 0x00, 0x40, 0x7d, 0x5e, 0xf4, 0xb3, 0x42, 0x65, 0xbf, 0xe7, 0x99, 0x0e, 0x81, 0xdf, |
|
| 1366 |
- 0x32, 0xd9, 0xb2, 0x77, 0x02, 0x3b, 0x4b, 0x3c, 0x57, 0x94, 0xa9, 0xef, 0xd8, 0x65, 0xaa, 0xb9, |
|
| 1367 |
- 0x7f, 0x9b, 0x59, 0xda, 0x47, 0xed, 0xda, 0xf5, 0xe7, 0x12, 0x7d, 0x8d, 0x98, 0x27, 0x17, 0xe7, |
|
| 1368 |
- 0x19, 0xb4, 0x43, 0x1a, 0xbe, 0x8c, 0x03, 0x2a, 0x16, 0x23, 0x7b, 0x30, 0xf3, 0x5a, 0xa1, 0x3d, |
|
| 1369 |
- 0xa6, 0xa1, 0x23, 0x06, 0xda, 0x02, 0x2b, 0x1d, 0x61, 0x19, 0xc7, 0x6b, 0x0e, 0x2c, 0x6f, 0x17, |
|
| 1370 |
- 0x86, 0xc1, 0xca, 0xe2, 0x30, 0xc8, 0x8f, 0x06, 0xeb, 0xde, 0x18, 0xf7, 0xff, 0x55, 0x87, 0xca, |
|
| 1371 |
- 0xc7, 0x47, 0xaf, 0x9c, 0x53, 0xd8, 0x5e, 0x7c, 0x40, 0x77, 0x1e, 0xb2, 0xe8, 0x35, 0x8f, 0xee, |
|
| 1372 |
- 0xbd, 0xf7, 0xd6, 0xee, 0xf3, 0xb4, 0x7c, 0xcb, 0xf1, 0x60, 0x6b, 0xe1, 0x41, 0xd6, 0xc9, 0xda, |
|
| 1373 |
- 0xc9, 0xea, 0x27, 0xe9, 0xde, 0xc3, 0x75, 0xdb, 0x36, 0xcf, 0x85, 0xf1, 0xdc, 0xf0, 0x5c, 0xfd, |
|
| 1374 |
- 0xa9, 0x66, 0x78, 0xae, 0x9b, 0xea, 0x6f, 0x39, 0x3f, 0x82, 0x3a, 0x3d, 0xd1, 0x3a, 0x77, 0x98, |
|
| 1375 |
- 0xb6, 0xf0, 0xf8, 0xdb, 0xbb, 0xbb, 0x80, 0x35, 0x07, 0x5f, 0x43, 0xbb, 0xf0, 0xea, 0xee, 0xbc, |
|
| 1376 |
- 0x53, 0x90, 0x55, 0x7c, 0xe1, 0xed, 0xbd, 0xbb, 0x7a, 0xd3, 0x70, 0x3b, 0x00, 0xc8, 0x5f, 0xf1, |
|
| 1377 |
- 0x9c, 0x2e, 0x53, 0x2f, 0xbd, 0x14, 0xf7, 0xee, 0xaf, 0xd8, 0x31, 0x4c, 0xd0, 0x95, 0x8b, 0xcf, |
|
| 1378 |
- 0x72, 0xce, 0x82, 0x55, 0x17, 0x9f, 0xce, 0x8c, 0x2b, 0xd7, 0xbe, 0xe7, 0x69, 0xb6, 0x8b, 0x4f, |
|
| 1379 |
- 0x72, 0x86, 0xed, 0x9a, 0xa7, 0x3e, 0xc3, 0x76, 0xed, 0x5b, 0xde, 0x2d, 0xe7, 0x57, 0xd0, 0x29, |
|
| 1380 |
- 0x3e, 0x92, 0x39, 0x99, 0x91, 0x56, 0x3e, 0xf2, 0xf5, 0x1e, 0xac, 0xd9, 0x35, 0x0c, 0x3f, 0x84, |
|
| 1381 |
- 0x1a, 0xbd, 0x7e, 0x65, 0x29, 0x67, 0x3f, 0x9a, 0xf5, 0xee, 0x14, 0x91, 0xe6, 0xd4, 0x13, 0xa8, |
|
| 1382 |
- 0xd3, 0x87, 0x9d, 0x09, 0x80, 0xc2, 0x77, 0x5e, 0xaf, 0x65, 0x63, 0xdd, 0x5b, 0x4f, 0x4a, 0x99, |
|
| 1383 |
- 0x9c, 0xb4, 0x20, 0x27, 0x5d, 0x25, 0xc7, 0x72, 0xce, 0x59, 0x5d, 0xff, 0xa3, 0xf5, 0xf4, 0x3f, |
|
| 1384 |
- 0x01, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xa3, 0xf6, 0xb8, 0xde, 0x1a, 0x00, 0x00, |
|
| 1241 |
+ // 2348 bytes of a gzipped FileDescriptorProto |
|
| 1242 |
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xec, 0x59, 0x4b, 0x73, 0x1c, 0x49, |
|
| 1243 |
+ 0x11, 0xf6, 0x3c, 0xa5, 0xc9, 0x79, 0x48, 0xea, 0xd5, 0x63, 0x3c, 0xbb, 0xf6, 0x9a, 0x8e, 0x05, |
|
| 1244 |
+ 0x0c, 0x2c, 0xc2, 0x8c, 0x77, 0x03, 0x07, 0x44, 0x10, 0x61, 0x4b, 0x66, 0x31, 0x6b, 0x2d, 0xe3, |
|
| 1245 |
+ 0x96, 0xc4, 0x1e, 0x27, 0x5a, 0xdd, 0xe5, 0x99, 0x46, 0x33, 0xdd, 0xbd, 0xdd, 0x35, 0xd2, 0xe8, |
|
| 1246 |
+ 0xc2, 0x81, 0x03, 0xdc, 0xf8, 0x03, 0x10, 0x5c, 0xb8, 0x71, 0xe7, 0xc0, 0x2f, 0x20, 0x82, 0x1f, |
|
| 1247 |
+ 0xc2, 0x8d, 0x3b, 0x47, 0xb2, 0xaa, 0xb2, 0xab, 0xab, 0xe7, 0x21, 0x99, 0x03, 0xc1, 0x85, 0xcb, |
|
| 1248 |
+ 0x44, 0xd5, 0x57, 0x59, 0x99, 0x59, 0xf9, 0xaa, 0xec, 0x1a, 0x68, 0xb8, 0x71, 0x70, 0x18, 0x27, |
|
| 1249 |
+ 0x11, 0x8f, 0xac, 0x1a, 0xbf, 0x89, 0x59, 0x6a, 0xdf, 0x87, 0x83, 0xcf, 0x18, 0x3f, 0x65, 0xc9, |
|
| 1250 |
+ 0x15, 0x4b, 0x7e, 0xc1, 0x92, 0x34, 0x88, 0x42, 0x87, 0x7d, 0x35, 0x63, 0x29, 0xb7, 0xe7, 0xd0, |
|
| 1251 |
+ 0x5d, 0x5e, 0x4a, 0xe3, 0x28, 0x4c, 0x99, 0xb5, 0x0b, 0xb5, 0xa9, 0xfb, 0xcb, 0x28, 0xe9, 0x96, |
|
| 1252 |
+ 0x1e, 0x95, 0x1e, 0xb7, 0x1d, 0x35, 0x91, 0x68, 0x10, 0x22, 0x5a, 0x26, 0x54, 0x4c, 0x04, 0x1a, |
|
| 1253 |
+ 0xbb, 0xdc, 0x1b, 0x77, 0x2b, 0x0a, 0x95, 0x13, 0xab, 0x07, 0x9b, 0x09, 0xbb, 0x0a, 0x04, 0xd7, |
|
| 1254 |
+ 0x6e, 0x15, 0x17, 0x1a, 0x8e, 0x9e, 0xdb, 0xbf, 0x29, 0xc1, 0xee, 0x79, 0xec, 0xbb, 0x9c, 0x0d, |
|
| 1255 |
+ 0x92, 0xc8, 0x63, 0x69, 0x4a, 0x2a, 0x59, 0x1d, 0x28, 0x07, 0xbe, 0x94, 0xd9, 0x70, 0x70, 0x64, |
|
| 1256 |
+ 0x6d, 0x43, 0x25, 0x46, 0xa0, 0x2c, 0x01, 0x31, 0xb4, 0x1e, 0x02, 0x78, 0x93, 0x28, 0x65, 0xa7, |
|
| 1257 |
+ 0xdc, 0x0f, 0x42, 0x29, 0x71, 0xd3, 0x31, 0x10, 0xa1, 0xcc, 0x75, 0xe0, 0xf3, 0xb1, 0x94, 0x89, |
|
| 1258 |
+ 0xca, 0xc8, 0x89, 0xb5, 0x0f, 0xf5, 0x31, 0x0b, 0x46, 0x63, 0xde, 0xad, 0x49, 0x98, 0x66, 0xf6, |
|
| 1259 |
+ 0x01, 0xec, 0x2d, 0xe8, 0xa1, 0xce, 0x6f, 0xff, 0xbd, 0x0c, 0xfb, 0x47, 0x09, 0xc3, 0x95, 0xa3, |
|
| 1260 |
+ 0x28, 0xe4, 0x6e, 0x10, 0xb2, 0x64, 0x9d, 0x8e, 0xa8, 0xd1, 0xc5, 0x2c, 0xf4, 0x27, 0x6c, 0xe0, |
|
| 1261 |
+ 0xa2, 0x58, 0xa5, 0xaa, 0x81, 0x48, 0x8d, 0xc7, 0xcc, 0xbb, 0x8c, 0xa3, 0x20, 0xe4, 0x52, 0x63, |
|
| 1262 |
+ 0x5c, 0xcf, 0x11, 0xa1, 0x71, 0x2a, 0x0f, 0xa3, 0xac, 0xa4, 0x26, 0x42, 0x63, 0x1c, 0x44, 0x33, |
|
| 1263 |
+ 0xa5, 0x71, 0xc3, 0xa1, 0x19, 0xe1, 0x2c, 0x49, 0xba, 0x75, 0x8d, 0xe3, 0x4c, 0xe0, 0x13, 0xf7, |
|
| 1264 |
+ 0x82, 0x4d, 0xd2, 0xee, 0xc6, 0xa3, 0x8a, 0xc0, 0xd5, 0xcc, 0x7a, 0x04, 0xcd, 0x30, 0x1a, 0x04, |
|
| 1265 |
+ 0x57, 0x11, 0x77, 0xa2, 0x88, 0x77, 0x37, 0xa5, 0xc1, 0x4c, 0xc8, 0xea, 0xc2, 0x46, 0x32, 0x0b, |
|
| 1266 |
+ 0x79, 0x30, 0x65, 0xdd, 0x86, 0x64, 0x99, 0x4d, 0xc5, 0x5e, 0x1a, 0x3e, 0x4f, 0x46, 0x69, 0x17, |
|
| 1267 |
+ 0x24, 0x63, 0x13, 0xb2, 0x3e, 0x82, 0x76, 0x7e, 0x92, 0xe3, 0x20, 0xe9, 0x36, 0x25, 0x87, 0x22, |
|
| 1268 |
+ 0x68, 0xbf, 0x82, 0x83, 0x25, 0x5b, 0x52, 0x9c, 0x1d, 0x42, 0xc3, 0xcb, 0x40, 0x69, 0xd3, 0x66, |
|
| 1269 |
+ 0x7f, 0xfb, 0x50, 0x46, 0xee, 0x61, 0x4e, 0x9c, 0x93, 0x20, 0xab, 0xf6, 0x69, 0x30, 0x0a, 0xdd, |
|
| 1270 |
+ 0xc9, 0xbb, 0x47, 0x8c, 0xb0, 0x98, 0xdc, 0x42, 0xf1, 0x49, 0x33, 0x7b, 0x1b, 0x3a, 0x19, 0x2b, |
|
| 1271 |
+ 0x72, 0xfa, 0x5f, 0x2a, 0xb0, 0xf3, 0xdc, 0xf7, 0xef, 0x88, 0x49, 0x0c, 0x6c, 0xce, 0x12, 0x0c, |
|
| 1272 |
+ 0x7d, 0xe4, 0x58, 0x96, 0xe6, 0xd4, 0x73, 0xeb, 0x43, 0xa8, 0xce, 0x52, 0x3c, 0x49, 0x45, 0x9e, |
|
| 1273 |
+ 0xa4, 0x49, 0x27, 0x39, 0x47, 0xc8, 0x91, 0x0b, 0x96, 0x05, 0x55, 0x57, 0xd8, 0xb2, 0x2a, 0x6d, |
|
| 1274 |
+ 0x29, 0xc7, 0x42, 0x65, 0x16, 0x5e, 0xa1, 0x9f, 0x05, 0x24, 0x86, 0x02, 0xf1, 0xae, 0x7d, 0xf2, |
|
| 1275 |
+ 0xb0, 0x18, 0x66, 0xc7, 0xda, 0xc8, 0x8f, 0xa5, 0xc3, 0x66, 0x73, 0x75, 0xd8, 0x34, 0xd6, 0x84, |
|
| 1276 |
+ 0x0d, 0x14, 0xc2, 0xc6, 0x86, 0x96, 0xe7, 0xc6, 0xee, 0x45, 0x30, 0x09, 0x78, 0xc0, 0x52, 0xf4, |
|
| 1277 |
+ 0x9f, 0x50, 0xa2, 0x80, 0x59, 0x8f, 0x61, 0xcb, 0x8d, 0x63, 0x37, 0x99, 0x46, 0x09, 0x9a, 0xe6, |
|
| 1278 |
+ 0x6d, 0x30, 0x61, 0xdd, 0x96, 0x64, 0xb2, 0x08, 0x0b, 0x6e, 0x29, 0x9b, 0x04, 0xe1, 0x6c, 0xfe, |
|
| 1279 |
+ 0x5a, 0x44, 0x5f, 0xb7, 0x2d, 0xc9, 0x0a, 0x98, 0xe0, 0x16, 0x46, 0x5f, 0xb0, 0xeb, 0x41, 0x12, |
|
| 1280 |
+ 0x5c, 0xe1, 0x9e, 0x11, 0x0a, 0xed, 0x48, 0x2b, 0x2e, 0xc2, 0xd6, 0x37, 0x31, 0x30, 0x27, 0xc1, |
|
| 1281 |
+ 0x34, 0xe0, 0x69, 0x77, 0x0b, 0xd5, 0x6a, 0xf6, 0xdb, 0x64, 0x4f, 0x47, 0xa2, 0x4e, 0xb6, 0x6a, |
|
| 1282 |
+ 0x1f, 0x43, 0x5d, 0x41, 0xc2, 0xbc, 0x82, 0x84, 0xbc, 0x25, 0xc7, 0x02, 0x4b, 0xa3, 0xb7, 0x5c, |
|
| 1283 |
+ 0xfa, 0xaa, 0xea, 0xc8, 0xb1, 0xc0, 0xc6, 0x6e, 0xe2, 0x4b, 0x3f, 0x21, 0x26, 0xc6, 0xb6, 0x03, |
|
| 1284 |
+ 0x55, 0xe1, 0x28, 0x61, 0xea, 0x19, 0x39, 0xbc, 0xed, 0x88, 0xa1, 0x40, 0x46, 0x14, 0x53, 0x88, |
|
| 1285 |
+ 0xe0, 0xd0, 0xfa, 0x06, 0x74, 0x5c, 0xdf, 0x47, 0xf3, 0x44, 0xe8, 0xf5, 0xcf, 0x02, 0x3f, 0x45, |
|
| 1286 |
+ 0x4e, 0x15, 0x5c, 0x5c, 0x40, 0xed, 0x5d, 0xb0, 0xcc, 0x80, 0xa2, 0x38, 0xfb, 0x75, 0x49, 0x27, |
|
| 1287 |
+ 0x84, 0xce, 0x93, 0x75, 0xd1, 0xf6, 0xfd, 0x42, 0xf5, 0x28, 0xcb, 0xb8, 0xda, 0xc9, 0x32, 0x24, |
|
| 1288 |
+ 0xdf, 0x6d, 0x16, 0x94, 0xa5, 0xa4, 0xac, 0xac, 0x4a, 0xca, 0x1e, 0x74, 0x97, 0x75, 0x20, 0x05, |
|
| 1289 |
+ 0x3d, 0x38, 0x38, 0x66, 0x13, 0xf6, 0x2e, 0xfa, 0xa1, 0x25, 0x43, 0x17, 0x4b, 0x87, 0x4a, 0x38, |
|
| 1290 |
+ 0x39, 0x7e, 0x77, 0x05, 0x96, 0x85, 0x90, 0x02, 0x27, 0xb0, 0xf7, 0x3a, 0x48, 0xf9, 0xdd, 0xe2, |
|
| 1291 |
+ 0x97, 0x44, 0x95, 0x57, 0x89, 0xfa, 0x15, 0x40, 0xce, 0x4a, 0xab, 0x5c, 0x32, 0x54, 0x46, 0x8c, |
|
| 1292 |
+ 0xcd, 0x03, 0x4e, 0x09, 0x2d, 0xc7, 0xc2, 0xed, 0xdc, 0x8b, 0xe9, 0x8e, 0x11, 0x43, 0x51, 0x10, |
|
| 1293 |
+ 0x67, 0x61, 0x30, 0x3f, 0x8d, 0xbc, 0x4b, 0xc6, 0x53, 0x59, 0xb0, 0xb1, 0x98, 0x1a, 0x90, 0xcc, |
|
| 1294 |
+ 0xca, 0x31, 0x9b, 0x4c, 0x64, 0xd5, 0xde, 0x74, 0xd4, 0x04, 0x8f, 0xb3, 0xbf, 0x78, 0x1c, 0xaa, |
|
| 1295 |
+ 0x7f, 0x4f, 0xa1, 0x99, 0xab, 0x9a, 0xa2, 0x4a, 0x95, 0xd5, 0xfe, 0x35, 0xa9, 0xec, 0x87, 0xd0, |
|
| 1296 |
+ 0x3a, 0xe5, 0xe8, 0xb9, 0x35, 0x46, 0xb1, 0x1f, 0x43, 0x47, 0x17, 0x4f, 0x49, 0xa8, 0xd2, 0xdf, |
|
| 1297 |
+ 0xe5, 0xb3, 0x94, 0xa8, 0x68, 0x66, 0xff, 0xb5, 0x02, 0x1b, 0x14, 0x9d, 0x59, 0x89, 0x29, 0xe5, |
|
| 1298 |
+ 0x25, 0xe6, 0x7f, 0x52, 0xe9, 0x3e, 0x80, 0x46, 0x7a, 0x93, 0x72, 0x36, 0x1d, 0x50, 0xbd, 0x6b, |
|
| 1299 |
+ 0x3b, 0x39, 0xf0, 0xff, 0xaa, 0x97, 0x57, 0xbd, 0xbf, 0x95, 0xa0, 0xa1, 0xdd, 0xfc, 0x1f, 0x77, |
|
| 1300 |
+ 0x25, 0x1f, 0x43, 0x23, 0x56, 0x8e, 0x67, 0xaa, 0x78, 0x35, 0xfb, 0x1d, 0x12, 0x94, 0x95, 0xab, |
|
| 1301 |
+ 0x9c, 0xc0, 0x88, 0x9f, 0xaa, 0x19, 0x3f, 0x46, 0xd7, 0x51, 0x2b, 0x74, 0x1d, 0xe8, 0xfc, 0x58, |
|
| 1302 |
+ 0x54, 0xc5, 0xba, 0xac, 0x8a, 0x72, 0x6c, 0xf6, 0x19, 0x1b, 0x85, 0x3e, 0xc3, 0xfe, 0x14, 0x36, |
|
| 1303 |
+ 0x4e, 0x5c, 0x6f, 0x8c, 0xe7, 0x10, 0x1b, 0xbd, 0x98, 0xc2, 0x14, 0x37, 0x8a, 0xb1, 0x10, 0x32, |
|
| 1304 |
+ 0x65, 0x68, 0xef, 0x1b, 0x2a, 0xe1, 0x34, 0xb3, 0x2f, 0xb1, 0x17, 0x50, 0x69, 0x40, 0xc9, 0xf4, |
|
| 1305 |
+ 0x04, 0x6b, 0x65, 0x66, 0x90, 0x2c, 0x97, 0x96, 0xbb, 0x09, 0x83, 0x06, 0xdd, 0xb2, 0x31, 0x55, |
|
| 1306 |
+ 0x92, 0xa9, 0xb4, 0x66, 0x36, 0x20, 0x7d, 0x9c, 0x6c, 0xd9, 0xfe, 0x6d, 0x09, 0xf6, 0x55, 0xab, |
|
| 1307 |
+ 0x78, 0x67, 0x43, 0xb8, 0xba, 0x05, 0x51, 0xe6, 0xab, 0x14, 0xcc, 0xf7, 0x14, 0x1a, 0x09, 0x4b, |
|
| 1308 |
+ 0xa3, 0x59, 0x82, 0x66, 0x96, 0x96, 0x6d, 0xf6, 0xf7, 0xb2, 0x4c, 0x92, 0xb2, 0x1c, 0x5a, 0x75, |
|
| 1309 |
+ 0x72, 0x3a, 0xfb, 0x0f, 0x15, 0xe8, 0x14, 0x57, 0x45, 0x5d, 0xba, 0x98, 0x5c, 0x06, 0xd1, 0x97, |
|
| 1310 |
+ 0xaa, 0xc7, 0x55, 0xc6, 0x33, 0x21, 0x91, 0x55, 0x68, 0xcb, 0x53, 0xbc, 0xe8, 0x50, 0x92, 0xba, |
|
| 1311 |
+ 0xc8, 0x72, 0x80, 0x56, 0x07, 0x2c, 0x09, 0x22, 0x9f, 0xba, 0xa4, 0x1c, 0x10, 0x65, 0x00, 0x27, |
|
| 1312 |
+ 0x6f, 0x66, 0x11, 0x77, 0xa9, 0xab, 0xd6, 0x73, 0xd9, 0xdc, 0xa2, 0x8f, 0x18, 0x3f, 0x12, 0x5e, |
|
| 1313 |
+ 0xab, 0x51, 0x73, 0xab, 0x91, 0x7c, 0xfd, 0x84, 0x4d, 0x53, 0x4a, 0x73, 0x03, 0x11, 0x9a, 0x2b, |
|
| 1314 |
+ 0x6f, 0xbe, 0x16, 0x41, 0x4d, 0xf9, 0x6e, 0x42, 0x82, 0x83, 0x9a, 0x9e, 0x5e, 0xbb, 0xb1, 0x4c, |
|
| 1315 |
+ 0xfb, 0xb6, 0x63, 0x20, 0x18, 0xc8, 0x3b, 0x6a, 0x86, 0xd6, 0xc0, 0x4f, 0x19, 0x57, 0xdc, 0xbe, |
|
| 1316 |
+ 0xb2, 0x0c, 0xb4, 0x9d, 0xe5, 0x05, 0x41, 0x7d, 0xc9, 0x92, 0x90, 0x4d, 0x4e, 0x0c, 0xa9, 0xa0, |
|
| 1317 |
+ 0xa8, 0x97, 0x16, 0xac, 0x3e, 0xec, 0x2a, 0xf0, 0xec, 0x68, 0x60, 0x6e, 0x68, 0xca, 0x0d, 0x2b, |
|
| 1318 |
+ 0xd7, 0xc4, 0x07, 0xd7, 0x52, 0x9c, 0xd0, 0xad, 0xf6, 0x5d, 0x68, 0xbf, 0xbc, 0x62, 0x58, 0xc1, |
|
| 1319 |
+ 0xb3, 0xc8, 0x41, 0xbb, 0x8b, 0x04, 0xc0, 0x68, 0x98, 0xc6, 0xd2, 0x6b, 0x55, 0x27, 0x07, 0xec, |
|
| 1320 |
+ 0x14, 0x6a, 0x92, 0x7c, 0x65, 0x57, 0xa3, 0x82, 0xae, 0xac, 0x83, 0xae, 0x18, 0x62, 0x6d, 0x1d, |
|
| 1321 |
+ 0x62, 0x14, 0x8c, 0xd5, 0x3c, 0x18, 0x0b, 0x42, 0x6b, 0x8b, 0x42, 0x7f, 0x57, 0x86, 0xd6, 0x17, |
|
| 1322 |
+ 0x8c, 0x5f, 0x47, 0xc9, 0xa5, 0x48, 0xae, 0x74, 0xe5, 0x6d, 0x79, 0x1f, 0xbf, 0xed, 0xe6, 0xc3, |
|
| 1323 |
+ 0x8b, 0x1b, 0x4e, 0xc1, 0x54, 0xc5, 0x5c, 0x9e, 0xbf, 0x10, 0x53, 0xeb, 0x01, 0x00, 0x2e, 0x0d, |
|
| 1324 |
+ 0x5c, 0x75, 0x43, 0xaa, 0xfe, 0xaa, 0x91, 0xcc, 0x09, 0xb0, 0xde, 0x87, 0x86, 0x33, 0x1f, 0x62, |
|
| 1325 |
+ 0x0d, 0x8e, 0x12, 0x15, 0xf1, 0x55, 0xfc, 0x2c, 0x9c, 0xbf, 0x94, 0x73, 0xb1, 0x17, 0x17, 0xfd, |
|
| 1326 |
+ 0x24, 0x8a, 0x63, 0xe6, 0x67, 0xaa, 0x25, 0xf3, 0x63, 0x05, 0x08, 0xa9, 0x67, 0x99, 0xd4, 0xba, |
|
| 1327 |
+ 0x92, 0xca, 0x73, 0xa9, 0xb8, 0x14, 0x93, 0xd4, 0x0d, 0x3a, 0x94, 0x29, 0xf5, 0x4c, 0x4b, 0xdd, |
|
| 1328 |
+ 0x54, 0x52, 0xb9, 0x21, 0xf5, 0x2c, 0x97, 0xda, 0xc8, 0xf6, 0x92, 0x54, 0xfb, 0xcf, 0x25, 0xd8, |
|
| 1329 |
+ 0xc4, 0x50, 0x3e, 0x4f, 0xdd, 0x11, 0xc3, 0x5b, 0xaf, 0xc9, 0x31, 0xec, 0x27, 0xc3, 0x99, 0x98, |
|
| 1330 |
+ 0x92, 0xcb, 0x40, 0x42, 0x8a, 0xe0, 0x6b, 0xd0, 0x8a, 0x59, 0x82, 0x01, 0x4e, 0x14, 0x65, 0x2c, |
|
| 1331 |
+ 0x42, 0x55, 0xa7, 0xa9, 0x30, 0x45, 0x72, 0x08, 0xef, 0xc9, 0xb5, 0x61, 0x10, 0x0e, 0x55, 0x04, |
|
| 1332 |
+ 0x4d, 0x23, 0x9f, 0x91, 0xa9, 0x76, 0xe4, 0xd2, 0xab, 0xf0, 0x73, 0xbd, 0x60, 0x7d, 0x1b, 0x76, |
|
| 1333 |
+ 0x34, 0xbd, 0xb8, 0x59, 0x25, 0xb5, 0x32, 0xdd, 0x16, 0x51, 0x9f, 0x13, 0x8c, 0x8d, 0x4e, 0xe7, |
|
| 1334 |
+ 0x6c, 0x8c, 0x9f, 0xff, 0x1c, 0xaf, 0x9e, 0xd1, 0xb1, 0x8b, 0x09, 0x8a, 0x55, 0x37, 0x96, 0x69, |
|
| 1335 |
+ 0x9c, 0x92, 0xb6, 0xd9, 0xd4, 0xfa, 0x0e, 0xec, 0x70, 0x45, 0xcb, 0xfc, 0x61, 0x46, 0xa3, 0xbc, |
|
| 1336 |
+ 0xb9, 0xad, 0x17, 0x06, 0x44, 0xfc, 0x75, 0xe8, 0xe4, 0xc4, 0xb2, 0x86, 0x2b, 0x7d, 0xdb, 0x1a, |
|
| 1337 |
+ 0x3d, 0x13, 0x95, 0xfc, 0xf7, 0xca, 0x58, 0x2a, 0x72, 0x3e, 0x96, 0x55, 0xc5, 0x30, 0x55, 0xb3, |
|
| 1338 |
+ 0xbf, 0x95, 0x55, 0x63, 0x32, 0x86, 0xac, 0x24, 0xca, 0x2c, 0x3f, 0x86, 0x2d, 0xae, 0x55, 0x1f, |
|
| 1339 |
+ 0x62, 0x02, 0xb9, 0x54, 0x92, 0xb3, 0x8a, 0x58, 0x3c, 0x98, 0xd3, 0xe1, 0xc5, 0x83, 0xa2, 0xe5, |
|
| 1340 |
+ 0x55, 0x9b, 0x40, 0x02, 0x95, 0x7e, 0x4d, 0x85, 0x49, 0x11, 0xf6, 0x8f, 0xa0, 0x81, 0x3d, 0x44, |
|
| 1341 |
+ 0xaa, 0xb4, 0x43, 0xc3, 0x78, 0xb3, 0x24, 0xc1, 0xfc, 0xca, 0x0c, 0x43, 0x53, 0xd1, 0x63, 0xc8, |
|
| 1342 |
+ 0x2b, 0x96, 0x8c, 0xa1, 0x26, 0x76, 0x04, 0xa0, 0xd2, 0x5c, 0x4a, 0x43, 0x1a, 0x33, 0x04, 0xd4, |
|
| 1343 |
+ 0x44, 0xc4, 0xd9, 0xd4, 0x9d, 0x6b, 0xd7, 0xcb, 0x38, 0x43, 0x40, 0x1d, 0x10, 0x05, 0xbe, 0x75, |
|
| 1344 |
+ 0x83, 0x89, 0x47, 0x8f, 0x00, 0x28, 0x90, 0xa6, 0xb9, 0xc0, 0xaa, 0x29, 0xf0, 0x4f, 0x65, 0x68, |
|
| 1345 |
+ 0x2a, 0x89, 0x4a, 0x61, 0xa4, 0xf2, 0xf0, 0x32, 0xd2, 0x22, 0xe5, 0x04, 0xdb, 0x85, 0x5a, 0x2e, |
|
| 1346 |
+ 0x2e, 0x6f, 0x1d, 0x73, 0x55, 0x33, 0xdd, 0xf0, 0x72, 0x4c, 0xb1, 0x5e, 0x1a, 0xd6, 0x59, 0x49, |
|
| 1347 |
+ 0xdd, 0x10, 0x44, 0x4a, 0xe1, 0x4f, 0xa0, 0xa5, 0xe2, 0x93, 0xf6, 0x54, 0xd7, 0xed, 0x69, 0x2a, |
|
| 1348 |
+ 0x32, 0xb5, 0xeb, 0xa9, 0xe8, 0xd0, 0x50, 0x5f, 0xd9, 0x11, 0x34, 0xfb, 0x0f, 0x0a, 0xe4, 0xf2, |
|
| 1349 |
+ 0x24, 0x87, 0xf2, 0xf7, 0x65, 0xc8, 0xb1, 0x34, 0x2b, 0xda, 0xde, 0x33, 0x80, 0x1c, 0x14, 0x35, |
|
| 1350 |
+ 0xeb, 0x92, 0xdd, 0x64, 0x9d, 0x28, 0x0e, 0xc5, 0xd9, 0xaf, 0xdc, 0xc9, 0x2c, 0x33, 0xaa, 0x9a, |
|
| 1351 |
+ 0xfc, 0xb0, 0xfc, 0xac, 0x84, 0x9f, 0x2a, 0x5b, 0x2f, 0xc4, 0x3d, 0x67, 0x6c, 0x2f, 0xbc, 0x5d, |
|
| 1352 |
+ 0x55, 0x57, 0xbe, 0x5d, 0x55, 0xb3, 0xb7, 0x2b, 0x2c, 0xa3, 0x51, 0x4c, 0xb7, 0x32, 0x8e, 0x72, |
|
| 1353 |
+ 0x41, 0x55, 0x43, 0x90, 0xfd, 0x8f, 0x2a, 0x40, 0x2e, 0xc5, 0x3a, 0x85, 0x5e, 0x10, 0x0d, 0xc5, |
|
| 1354 |
+ 0xa5, 0x12, 0x78, 0x4c, 0x15, 0xa4, 0x61, 0xc2, 0x30, 0x7c, 0xd2, 0xe0, 0x8a, 0x51, 0xdf, 0xb1, |
|
| 1355 |
+ 0x4f, 0xe7, 0x5e, 0x50, 0xce, 0x39, 0xc0, 0x99, 0xda, 0x28, 0x2b, 0x97, 0x93, 0x6d, 0xb3, 0x7e, |
|
| 1356 |
+ 0x06, 0x7b, 0x39, 0x53, 0xdf, 0xe0, 0x57, 0xbe, 0x95, 0xdf, 0x7b, 0x9a, 0x9f, 0x9f, 0xf3, 0xfa, |
|
| 1357 |
+ 0x09, 0x20, 0x3c, 0xc4, 0x3b, 0x66, 0x56, 0xe0, 0x54, 0xb9, 0x95, 0xd3, 0x4e, 0x10, 0xbd, 0x91, |
|
| 1358 |
+ 0x3b, 0x72, 0x3e, 0x6f, 0xe0, 0xbe, 0x71, 0x50, 0x91, 0xf6, 0x06, 0xb7, 0xea, 0xad, 0xdc, 0xf6, |
|
| 1359 |
+ 0xb5, 0x5e, 0xa2, 0x30, 0xe4, 0x2c, 0x3f, 0x07, 0x5c, 0x19, 0x5e, 0xbb, 0x01, 0x5f, 0xe4, 0x57, |
|
| 1360 |
+ 0xbb, 0xeb, 0x9c, 0x5f, 0xe2, 0xa6, 0x22, 0x33, 0x75, 0xce, 0x29, 0x4b, 0x46, 0x85, 0x73, 0xd6, |
|
| 1361 |
+ 0xef, 0x3a, 0xe7, 0x89, 0xdc, 0x91, 0xf3, 0x79, 0x01, 0x08, 0x2e, 0xea, 0xb3, 0x71, 0x2b, 0x97, |
|
| 1362 |
+ 0xad, 0x20, 0x2a, 0xea, 0x72, 0x04, 0x3b, 0x29, 0xf3, 0x38, 0xde, 0x28, 0x06, 0x8f, 0xcd, 0x5b, |
|
| 1363 |
+ 0x79, 0x6c, 0xd3, 0x06, 0xcd, 0xc4, 0xfe, 0x0a, 0x5a, 0x3f, 0x9d, 0x8d, 0x18, 0x9f, 0x5c, 0xe8, |
|
| 1364 |
+ 0x9c, 0xff, 0x6f, 0x97, 0x99, 0x7f, 0x61, 0x99, 0x39, 0x1a, 0x25, 0xd1, 0x2c, 0x2e, 0x54, 0x6d, |
|
| 1365 |
+ 0x95, 0xc3, 0x4b, 0x55, 0x5b, 0xd2, 0xc8, 0xaa, 0xad, 0xa8, 0x3f, 0x85, 0x96, 0x6a, 0xb2, 0x68, |
|
| 1366 |
+ 0x83, 0xaa, 0x42, 0xd6, 0x72, 0xd2, 0x67, 0x4d, 0x9d, 0xda, 0xd6, 0xa7, 0x86, 0x95, 0x76, 0x15, |
|
| 1367 |
+ 0xab, 0x51, 0x6e, 0x26, 0xfc, 0x62, 0xc9, 0xb3, 0xee, 0x15, 0xb4, 0xc7, 0xca, 0x36, 0xb4, 0x4b, |
|
| 1368 |
+ 0x05, 0xe0, 0x47, 0x99, 0x72, 0xf9, 0x19, 0x0e, 0x4d, 0x1b, 0x2a, 0x53, 0xb7, 0xc6, 0xa6, 0x59, |
|
| 1369 |
+ 0xbf, 0x07, 0x20, 0x3e, 0x49, 0x86, 0x59, 0xa1, 0x32, 0x9f, 0x1d, 0xf5, 0x0d, 0x81, 0xdf, 0x3f, |
|
| 1370 |
+ 0xd9, 0xb0, 0x77, 0x06, 0x3b, 0x4b, 0x3c, 0x57, 0x94, 0xa9, 0x6f, 0x99, 0x65, 0xaa, 0xd9, 0x7f, |
|
| 1371 |
+ 0x8f, 0x58, 0x9a, 0x5b, 0xcd, 0xda, 0xf5, 0xc7, 0x92, 0xfa, 0x82, 0xd1, 0x2f, 0x43, 0xd6, 0x33, |
|
| 1372 |
+ 0x68, 0x87, 0xaa, 0xf9, 0xd2, 0x0e, 0xa8, 0x18, 0x8c, 0xcc, 0xc6, 0xcc, 0x69, 0x85, 0x66, 0x9b, |
|
| 1373 |
+ 0x86, 0x8e, 0xf0, 0xa4, 0x05, 0x56, 0x3a, 0xc2, 0x30, 0x8e, 0xd3, 0xf4, 0x0c, 0x6f, 0x17, 0x9a, |
|
| 1374 |
+ 0xc1, 0xca, 0x62, 0x33, 0x48, 0x0f, 0x0d, 0xeb, 0x9e, 0x42, 0xfb, 0xff, 0xac, 0x43, 0xe5, 0xf9, |
|
| 1375 |
+ 0xe0, 0x95, 0x75, 0x0e, 0xdb, 0x8b, 0xff, 0x24, 0x58, 0x0f, 0x49, 0xf4, 0x9a, 0x7f, 0x1f, 0x7a, |
|
| 1376 |
+ 0x1f, 0xae, 0x5d, 0xa7, 0x6e, 0xf9, 0x9e, 0xe5, 0xc0, 0xd6, 0xc2, 0xbb, 0xb1, 0x95, 0x5d, 0x27, |
|
| 1377 |
+ 0xab, 0xdf, 0xe6, 0x7b, 0x0f, 0xd7, 0x2d, 0x9b, 0x3c, 0x17, 0xda, 0x73, 0xcd, 0x73, 0xf5, 0xe7, |
|
| 1378 |
+ 0x9d, 0xe6, 0xb9, 0xae, 0xab, 0xbf, 0x67, 0xfd, 0x00, 0xea, 0xea, 0x25, 0xd9, 0xda, 0x25, 0xda, |
|
| 1379 |
+ 0xc2, 0x1b, 0x75, 0x6f, 0x6f, 0x01, 0xd5, 0x1b, 0x5f, 0x43, 0xbb, 0xf0, 0xf7, 0x83, 0xf5, 0x7e, |
|
| 1380 |
+ 0x41, 0x56, 0xf1, 0x21, 0xba, 0xf7, 0xc1, 0xea, 0x45, 0xcd, 0xed, 0x08, 0x20, 0x7f, 0x6c, 0xb4, |
|
| 1381 |
+ 0xba, 0x44, 0xbd, 0xf4, 0xa0, 0xdd, 0xbb, 0xbf, 0x62, 0x45, 0x33, 0x41, 0x57, 0x2e, 0x3e, 0x0b, |
|
| 1382 |
+ 0x5a, 0x0b, 0x56, 0x5d, 0x7c, 0x94, 0xd3, 0xae, 0x5c, 0xfb, 0x9e, 0x28, 0xd9, 0x2e, 0x3e, 0xf6, |
|
| 1383 |
+ 0x69, 0xb6, 0x6b, 0x9e, 0x1a, 0x35, 0xdb, 0xb5, 0xaf, 0x84, 0xf7, 0xac, 0x9f, 0x43, 0xa7, 0xf8, |
|
| 1384 |
+ 0xb0, 0x66, 0x65, 0x46, 0x5a, 0xf9, 0x7c, 0xd8, 0x7b, 0xb0, 0x66, 0x55, 0x33, 0xfc, 0x04, 0x6a, |
|
| 1385 |
+ 0xea, 0xc5, 0x2c, 0x4b, 0x39, 0xf3, 0xa1, 0xad, 0xb7, 0x5b, 0x04, 0xf5, 0xae, 0x27, 0x50, 0x57, |
|
| 1386 |
+ 0x1f, 0x76, 0x3a, 0x00, 0x0a, 0xdf, 0x79, 0xbd, 0x96, 0x89, 0xda, 0xf7, 0x9e, 0x94, 0x32, 0x39, |
|
| 1387 |
+ 0x69, 0x41, 0x4e, 0xba, 0x4a, 0x8e, 0xe1, 0x9c, 0x8b, 0xba, 0xfc, 0x6b, 0xef, 0xe9, 0xbf, 0x03, |
|
| 1388 |
+ 0x00, 0x00, 0xff, 0xff, 0x58, 0xa9, 0x0a, 0x41, 0xe7, 0x1b, 0x00, 0x00, |
|
| 1385 | 1389 |
} |
| ... | ... |
@@ -47,6 +47,9 @@ message CreateContainerRequest {
|
| 47 | 47 |
string stderr = 6; // path to file where stderr will be written (optional) |
| 48 | 48 |
repeated string labels = 7; |
| 49 | 49 |
bool noPivotRoot = 8; |
| 50 |
+ string runtime = 9; |
|
| 51 |
+ repeated string runtimeArgs = 10; |
|
| 52 |
+ string checkpointDir = 11; // Directory where checkpoints are stored |
|
| 50 | 53 |
} |
| 51 | 54 |
|
| 52 | 55 |
message CreateContainerResponse {
|
| ... | ... |
@@ -98,6 +101,7 @@ message AddProcessResponse {
|
| 98 | 98 |
message CreateCheckpointRequest {
|
| 99 | 99 |
string id = 1; // ID of container |
| 100 | 100 |
Checkpoint checkpoint = 2; // Checkpoint configuration |
| 101 |
+ string checkpointDir = 3; // Directory where checkpoints are stored |
|
| 101 | 102 |
} |
| 102 | 103 |
|
| 103 | 104 |
message CreateCheckpointResponse {
|
| ... | ... |
@@ -106,6 +110,7 @@ message CreateCheckpointResponse {
|
| 106 | 106 |
message DeleteCheckpointRequest {
|
| 107 | 107 |
string id = 1; // ID of container |
| 108 | 108 |
string name = 2; // Name of checkpoint |
| 109 |
+ string checkpointDir = 3; // Directory where checkpoints are stored |
|
| 109 | 110 |
} |
| 110 | 111 |
|
| 111 | 112 |
message DeleteCheckpointResponse {
|
| ... | ... |
@@ -113,6 +118,7 @@ message DeleteCheckpointResponse {
|
| 113 | 113 |
|
| 114 | 114 |
message ListCheckpointRequest {
|
| 115 | 115 |
string id = 1; // ID of container |
| 116 |
+ string checkpointDir = 2; // Directory where checkpoints are stored |
|
| 116 | 117 |
} |
| 117 | 118 |
|
| 118 | 119 |
message Checkpoint {
|
| ... | ... |
@@ -193,6 +199,7 @@ message UpdateResource {
|
| 193 | 193 |
uint32 memorySwap = 8; |
| 194 | 194 |
uint32 memoryReservation = 9; |
| 195 | 195 |
uint32 kernelMemoryLimit = 10; |
| 196 |
+ uint32 kernelTCPMemoryLimit = 11; |
|
| 196 | 197 |
} |
| 197 | 198 |
|
| 198 | 199 |
message UpdateContainerResponse {
|
| ... | ... |
@@ -39,5 +39,5 @@ test: install generate-test-pbs |
| 39 | 39 |
generate-test-pbs: |
| 40 | 40 |
make install |
| 41 | 41 |
make -C testdata |
| 42 |
- protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto |
|
| 42 |
+ protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto |
|
| 43 | 43 |
make |
| ... | ... |
@@ -768,11 +768,10 @@ func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
|
| 768 | 768 |
} |
| 769 | 769 |
} |
| 770 | 770 |
keyelem, valelem := keyptr.Elem(), valptr.Elem() |
| 771 |
- if !keyelem.IsValid() {
|
|
| 772 |
- keyelem = reflect.Zero(p.mtype.Key()) |
|
| 773 |
- } |
|
| 774 |
- if !valelem.IsValid() {
|
|
| 775 |
- valelem = reflect.Zero(p.mtype.Elem()) |
|
| 771 |
+ if !keyelem.IsValid() || !valelem.IsValid() {
|
|
| 772 |
+ // We did not decode the key or the value in the map entry. |
|
| 773 |
+ // Either way, it's an invalid map entry. |
|
| 774 |
+ return fmt.Errorf("proto: bad map data: missing key/val")
|
|
| 776 | 775 |
} |
| 777 | 776 |
|
| 778 | 777 |
v.SetMapIndex(keyelem, valelem) |
| ... | ... |
@@ -64,10 +64,6 @@ var ( |
| 64 | 64 |
// a struct with a repeated field containing a nil element. |
| 65 | 65 |
errRepeatedHasNil = errors.New("proto: repeated field has nil element")
|
| 66 | 66 |
|
| 67 |
- // errOneofHasNil is the error returned if Marshal is called with |
|
| 68 |
- // a struct with a oneof field containing a nil element. |
|
| 69 |
- errOneofHasNil = errors.New("proto: oneof field has nil value")
|
|
| 70 |
- |
|
| 71 | 67 |
// ErrNil is the error returned if Marshal is called with nil. |
| 72 | 68 |
ErrNil = errors.New("proto: Marshal called with nil")
|
| 73 | 69 |
) |
| ... | ... |
@@ -1226,9 +1222,7 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
|
| 1226 | 1226 |
// Do oneof fields. |
| 1227 | 1227 |
if prop.oneofMarshaler != nil {
|
| 1228 | 1228 |
m := structPointer_Interface(base, prop.stype).(Message) |
| 1229 |
- if err := prop.oneofMarshaler(m, o); err == ErrNil {
|
|
| 1230 |
- return errOneofHasNil |
|
| 1231 |
- } else if err != nil {
|
|
| 1229 |
+ if err := prop.oneofMarshaler(m, o); err != nil {
|
|
| 1232 | 1230 |
return err |
| 1233 | 1231 |
} |
| 1234 | 1232 |
} |
| ... | ... |
@@ -175,93 +175,7 @@ type raw interface {
|
| 175 | 175 |
Bytes() []byte |
| 176 | 176 |
} |
| 177 | 177 |
|
| 178 |
-func requiresQuotes(u string) bool {
|
|
| 179 |
- // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. |
|
| 180 |
- for _, ch := range u {
|
|
| 181 |
- switch {
|
|
| 182 |
- case ch == '.' || ch == '/' || ch == '_': |
|
| 183 |
- continue |
|
| 184 |
- case '0' <= ch && ch <= '9': |
|
| 185 |
- continue |
|
| 186 |
- case 'A' <= ch && ch <= 'Z': |
|
| 187 |
- continue |
|
| 188 |
- case 'a' <= ch && ch <= 'z': |
|
| 189 |
- continue |
|
| 190 |
- default: |
|
| 191 |
- return true |
|
| 192 |
- } |
|
| 193 |
- } |
|
| 194 |
- return false |
|
| 195 |
-} |
|
| 196 |
- |
|
| 197 |
-// isAny reports whether sv is a google.protobuf.Any message |
|
| 198 |
-func isAny(sv reflect.Value) bool {
|
|
| 199 |
- type wkt interface {
|
|
| 200 |
- XXX_WellKnownType() string |
|
| 201 |
- } |
|
| 202 |
- t, ok := sv.Addr().Interface().(wkt) |
|
| 203 |
- return ok && t.XXX_WellKnownType() == "Any" |
|
| 204 |
-} |
|
| 205 |
- |
|
| 206 |
-// writeProto3Any writes an expanded google.protobuf.Any message. |
|
| 207 |
-// |
|
| 208 |
-// It returns (false, nil) if sv value can't be unmarshaled (e.g. because |
|
| 209 |
-// required messages are not linked in). |
|
| 210 |
-// |
|
| 211 |
-// It returns (true, error) when sv was written in expanded format or an error |
|
| 212 |
-// was encountered. |
|
| 213 |
-func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
|
| 214 |
- turl := sv.FieldByName("TypeUrl")
|
|
| 215 |
- val := sv.FieldByName("Value")
|
|
| 216 |
- if !turl.IsValid() || !val.IsValid() {
|
|
| 217 |
- return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
| 218 |
- } |
|
| 219 |
- |
|
| 220 |
- b, ok := val.Interface().([]byte) |
|
| 221 |
- if !ok {
|
|
| 222 |
- return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
| 223 |
- } |
|
| 224 |
- |
|
| 225 |
- parts := strings.Split(turl.String(), "/") |
|
| 226 |
- mt := MessageType(parts[len(parts)-1]) |
|
| 227 |
- if mt == nil {
|
|
| 228 |
- return false, nil |
|
| 229 |
- } |
|
| 230 |
- m := reflect.New(mt.Elem()) |
|
| 231 |
- if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
|
| 232 |
- return false, nil |
|
| 233 |
- } |
|
| 234 |
- w.Write([]byte("["))
|
|
| 235 |
- u := turl.String() |
|
| 236 |
- if requiresQuotes(u) {
|
|
| 237 |
- writeString(w, u) |
|
| 238 |
- } else {
|
|
| 239 |
- w.Write([]byte(u)) |
|
| 240 |
- } |
|
| 241 |
- if w.compact {
|
|
| 242 |
- w.Write([]byte("]:<"))
|
|
| 243 |
- } else {
|
|
| 244 |
- w.Write([]byte("]: <\n"))
|
|
| 245 |
- w.ind++ |
|
| 246 |
- } |
|
| 247 |
- if err := tm.writeStruct(w, m.Elem()); err != nil {
|
|
| 248 |
- return true, err |
|
| 249 |
- } |
|
| 250 |
- if w.compact {
|
|
| 251 |
- w.Write([]byte("> "))
|
|
| 252 |
- } else {
|
|
| 253 |
- w.ind-- |
|
| 254 |
- w.Write([]byte(">\n"))
|
|
| 255 |
- } |
|
| 256 |
- return true, nil |
|
| 257 |
-} |
|
| 258 |
- |
|
| 259 |
-func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|
| 260 |
- if tm.ExpandAny && isAny(sv) {
|
|
| 261 |
- if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
|
| 262 |
- return err |
|
| 263 |
- } |
|
| 264 |
- } |
|
| 178 |
+func writeStruct(w *textWriter, sv reflect.Value) error {
|
|
| 265 | 179 |
st := sv.Type() |
| 266 | 180 |
sprops := GetProperties(st) |
| 267 | 181 |
for i := 0; i < sv.NumField(); i++ {
|
| ... | ... |
@@ -313,7 +227,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
| 313 | 313 |
} |
| 314 | 314 |
continue |
| 315 | 315 |
} |
| 316 |
- if err := tm.writeAny(w, v, props); err != nil {
|
|
| 316 |
+ if err := writeAny(w, v, props); err != nil {
|
|
| 317 | 317 |
return err |
| 318 | 318 |
} |
| 319 | 319 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -355,7 +269,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
| 355 | 355 |
return err |
| 356 | 356 |
} |
| 357 | 357 |
} |
| 358 |
- if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
|
| 358 |
+ if err := writeAny(w, key, props.mkeyprop); err != nil {
|
|
| 359 | 359 |
return err |
| 360 | 360 |
} |
| 361 | 361 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -372,7 +286,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
| 372 | 372 |
return err |
| 373 | 373 |
} |
| 374 | 374 |
} |
| 375 |
- if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
|
| 375 |
+ if err := writeAny(w, val, props.mvalprop); err != nil {
|
|
| 376 | 376 |
return err |
| 377 | 377 |
} |
| 378 | 378 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -444,7 +358,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
| 444 | 444 |
} |
| 445 | 445 |
|
| 446 | 446 |
// Enums have a String method, so writeAny will work fine. |
| 447 |
- if err := tm.writeAny(w, fv, props); err != nil {
|
|
| 447 |
+ if err := writeAny(w, fv, props); err != nil {
|
|
| 448 | 448 |
return err |
| 449 | 449 |
} |
| 450 | 450 |
|
| ... | ... |
@@ -456,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
| 456 | 456 |
// Extensions (the XXX_extensions field). |
| 457 | 457 |
pv := sv.Addr() |
| 458 | 458 |
if pv.Type().Implements(extendableProtoType) {
|
| 459 |
- if err := tm.writeExtensions(w, pv); err != nil {
|
|
| 459 |
+ if err := writeExtensions(w, pv); err != nil {
|
|
| 460 | 460 |
return err |
| 461 | 461 |
} |
| 462 | 462 |
} |
| ... | ... |
@@ -486,7 +400,7 @@ func writeRaw(w *textWriter, b []byte) error {
|
| 486 | 486 |
} |
| 487 | 487 |
|
| 488 | 488 |
// writeAny writes an arbitrary field. |
| 489 |
-func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
| 489 |
+func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
| 490 | 490 |
v = reflect.Indirect(v) |
| 491 | 491 |
|
| 492 | 492 |
// Floats have special cases. |
| ... | ... |
@@ -535,15 +449,15 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert |
| 535 | 535 |
} |
| 536 | 536 |
} |
| 537 | 537 |
w.indent() |
| 538 |
- if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
| 539 |
- text, err := etm.MarshalText() |
|
| 538 |
+ if tm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
| 539 |
+ text, err := tm.MarshalText() |
|
| 540 | 540 |
if err != nil {
|
| 541 | 541 |
return err |
| 542 | 542 |
} |
| 543 | 543 |
if _, err = w.Write(text); err != nil {
|
| 544 | 544 |
return err |
| 545 | 545 |
} |
| 546 |
- } else if err := tm.writeStruct(w, v); err != nil {
|
|
| 546 |
+ } else if err := writeStruct(w, v); err != nil {
|
|
| 547 | 547 |
return err |
| 548 | 548 |
} |
| 549 | 549 |
w.unindent() |
| ... | ... |
@@ -687,7 +601,7 @@ func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
| 687 | 687 |
|
| 688 | 688 |
// writeExtensions writes all the extensions in pv. |
| 689 | 689 |
// pv is assumed to be a pointer to a protocol message struct that is extendable. |
| 690 |
-func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
| 690 |
+func writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
| 691 | 691 |
emap := extensionMaps[pv.Type().Elem()] |
| 692 | 692 |
ep := pv.Interface().(extendableProto) |
| 693 | 693 |
|
| ... | ... |
@@ -722,13 +636,13 @@ func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error |
| 722 | 722 |
|
| 723 | 723 |
// Repeated extensions will appear as a slice. |
| 724 | 724 |
if !desc.repeated() {
|
| 725 |
- if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
|
| 725 |
+ if err := writeExtension(w, desc.Name, pb); err != nil {
|
|
| 726 | 726 |
return err |
| 727 | 727 |
} |
| 728 | 728 |
} else {
|
| 729 | 729 |
v := reflect.ValueOf(pb) |
| 730 | 730 |
for i := 0; i < v.Len(); i++ {
|
| 731 |
- if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
| 731 |
+ if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
| 732 | 732 |
return err |
| 733 | 733 |
} |
| 734 | 734 |
} |
| ... | ... |
@@ -737,7 +651,7 @@ func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error |
| 737 | 737 |
return nil |
| 738 | 738 |
} |
| 739 | 739 |
|
| 740 |
-func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
| 740 |
+func writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
| 741 | 741 |
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
| 742 | 742 |
return err |
| 743 | 743 |
} |
| ... | ... |
@@ -746,7 +660,7 @@ func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface |
| 746 | 746 |
return err |
| 747 | 747 |
} |
| 748 | 748 |
} |
| 749 |
- if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
| 749 |
+ if err := writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
| 750 | 750 |
return err |
| 751 | 751 |
} |
| 752 | 752 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -773,13 +687,12 @@ func (w *textWriter) writeIndent() {
|
| 773 | 773 |
|
| 774 | 774 |
// TextMarshaler is a configurable text format marshaler. |
| 775 | 775 |
type TextMarshaler struct {
|
| 776 |
- Compact bool // use compact text format (one line). |
|
| 777 |
- ExpandAny bool // expand google.protobuf.Any messages of known types |
|
| 776 |
+ Compact bool // use compact text format (one line). |
|
| 778 | 777 |
} |
| 779 | 778 |
|
| 780 | 779 |
// Marshal writes a given protocol buffer in text format. |
| 781 | 780 |
// The only errors returned are from w. |
| 782 |
-func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
|
| 781 |
+func (m *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
|
| 783 | 782 |
val := reflect.ValueOf(pb) |
| 784 | 783 |
if pb == nil || val.IsNil() {
|
| 785 | 784 |
w.Write([]byte("<nil>"))
|
| ... | ... |
@@ -794,11 +707,11 @@ func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
| 794 | 794 |
aw := &textWriter{
|
| 795 | 795 |
w: ww, |
| 796 | 796 |
complete: true, |
| 797 |
- compact: tm.Compact, |
|
| 797 |
+ compact: m.Compact, |
|
| 798 | 798 |
} |
| 799 | 799 |
|
| 800 |
- if etm, ok := pb.(encoding.TextMarshaler); ok {
|
|
| 801 |
- text, err := etm.MarshalText() |
|
| 800 |
+ if tm, ok := pb.(encoding.TextMarshaler); ok {
|
|
| 801 |
+ text, err := tm.MarshalText() |
|
| 802 | 802 |
if err != nil {
|
| 803 | 803 |
return err |
| 804 | 804 |
} |
| ... | ... |
@@ -812,7 +725,7 @@ func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
| 812 | 812 |
} |
| 813 | 813 |
// Dereference the received pointer so we don't have outer < and >. |
| 814 | 814 |
v := reflect.Indirect(val) |
| 815 |
- if err := tm.writeStruct(aw, v); err != nil {
|
|
| 815 |
+ if err := writeStruct(aw, v); err != nil {
|
|
| 816 | 816 |
return err |
| 817 | 817 |
} |
| 818 | 818 |
if bw != nil {
|
| ... | ... |
@@ -822,9 +735,9 @@ func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
| 822 | 822 |
} |
| 823 | 823 |
|
| 824 | 824 |
// Text is the same as Marshal, but returns the string directly. |
| 825 |
-func (tm *TextMarshaler) Text(pb Message) string {
|
|
| 825 |
+func (m *TextMarshaler) Text(pb Message) string {
|
|
| 826 | 826 |
var buf bytes.Buffer |
| 827 |
- tm.Marshal(&buf, pb) |
|
| 827 |
+ m.Marshal(&buf, pb) |
|
| 828 | 828 |
return buf.String() |
| 829 | 829 |
} |
| 830 | 830 |
|
| ... | ... |
@@ -163,7 +163,7 @@ func (p *textParser) advance() {
|
| 163 | 163 |
p.cur.offset, p.cur.line = p.offset, p.line |
| 164 | 164 |
p.cur.unquoted = "" |
| 165 | 165 |
switch p.s[0] {
|
| 166 |
- case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
|
| 166 |
+ case '<', '>', '{', '}', ':', '[', ']', ';', ',':
|
|
| 167 | 167 |
// Single symbol |
| 168 | 168 |
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] |
| 169 | 169 |
case '"', '\'': |
| ... | ... |
@@ -451,10 +451,7 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
| 451 | 451 |
fieldSet := make(map[string]bool) |
| 452 | 452 |
// A struct is a sequence of "name: value", terminated by one of |
| 453 | 453 |
// '>' or '}', or the end of the input. A name may also be |
| 454 |
- // "[extension]" or "[type/url]". |
|
| 455 |
- // |
|
| 456 |
- // The whole struct can also be an expanded Any message, like: |
|
| 457 |
- // [type/url] < ... struct contents ... > |
|
| 454 |
+ // "[extension]". |
|
| 458 | 455 |
for {
|
| 459 | 456 |
tok := p.next() |
| 460 | 457 |
if tok.err != nil {
|
| ... | ... |
@@ -464,66 +461,33 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
| 464 | 464 |
break |
| 465 | 465 |
} |
| 466 | 466 |
if tok.value == "[" {
|
| 467 |
- // Looks like an extension or an Any. |
|
| 467 |
+ // Looks like an extension. |
|
| 468 | 468 |
// |
| 469 | 469 |
// TODO: Check whether we need to handle |
| 470 | 470 |
// namespace rooted names (e.g. ".something.Foo"). |
| 471 |
- extName, err := p.consumeExtName() |
|
| 472 |
- if err != nil {
|
|
| 473 |
- return err |
|
| 471 |
+ tok = p.next() |
|
| 472 |
+ if tok.err != nil {
|
|
| 473 |
+ return tok.err |
|
| 474 | 474 |
} |
| 475 |
- |
|
| 476 |
- if s := strings.LastIndex(extName, "/"); s >= 0 {
|
|
| 477 |
- // If it contains a slash, it's an Any type URL. |
|
| 478 |
- messageName := extName[s+1:] |
|
| 479 |
- mt := MessageType(messageName) |
|
| 480 |
- if mt == nil {
|
|
| 481 |
- return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
|
|
| 482 |
- } |
|
| 483 |
- tok = p.next() |
|
| 484 |
- if tok.err != nil {
|
|
| 485 |
- return tok.err |
|
| 486 |
- } |
|
| 487 |
- // consume an optional colon |
|
| 488 |
- if tok.value == ":" {
|
|
| 489 |
- tok = p.next() |
|
| 490 |
- if tok.err != nil {
|
|
| 491 |
- return tok.err |
|
| 492 |
- } |
|
| 493 |
- } |
|
| 494 |
- var terminator string |
|
| 495 |
- switch tok.value {
|
|
| 496 |
- case "<": |
|
| 497 |
- terminator = ">" |
|
| 498 |
- case "{":
|
|
| 499 |
- terminator = "}" |
|
| 500 |
- default: |
|
| 501 |
- return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
| 502 |
- } |
|
| 503 |
- v := reflect.New(mt.Elem()) |
|
| 504 |
- if pe := p.readStruct(v.Elem(), terminator); pe != nil {
|
|
| 505 |
- return pe |
|
| 506 |
- } |
|
| 507 |
- b, err := Marshal(v.Interface().(Message)) |
|
| 508 |
- if err != nil {
|
|
| 509 |
- return p.errorf("failed to marshal message of type %q: %v", messageName, err)
|
|
| 510 |
- } |
|
| 511 |
- sv.FieldByName("TypeUrl").SetString(extName)
|
|
| 512 |
- sv.FieldByName("Value").SetBytes(b)
|
|
| 513 |
- continue |
|
| 514 |
- } |
|
| 515 |
- |
|
| 516 | 475 |
var desc *ExtensionDesc |
| 517 | 476 |
// This could be faster, but it's functional. |
| 518 | 477 |
// TODO: Do something smarter than a linear scan. |
| 519 | 478 |
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
| 520 |
- if d.Name == extName {
|
|
| 479 |
+ if d.Name == tok.value {
|
|
| 521 | 480 |
desc = d |
| 522 | 481 |
break |
| 523 | 482 |
} |
| 524 | 483 |
} |
| 525 | 484 |
if desc == nil {
|
| 526 |
- return p.errorf("unrecognized extension %q", extName)
|
|
| 485 |
+ return p.errorf("unrecognized extension %q", tok.value)
|
|
| 486 |
+ } |
|
| 487 |
+ // Check the extension terminator. |
|
| 488 |
+ tok = p.next() |
|
| 489 |
+ if tok.err != nil {
|
|
| 490 |
+ return tok.err |
|
| 491 |
+ } |
|
| 492 |
+ if tok.value != "]" {
|
|
| 493 |
+ return p.errorf("unrecognized extension terminator %q", tok.value)
|
|
| 527 | 494 |
} |
| 528 | 495 |
|
| 529 | 496 |
props := &Properties{}
|
| ... | ... |
@@ -679,35 +643,6 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
| 679 | 679 |
return reqFieldErr |
| 680 | 680 |
} |
| 681 | 681 |
|
| 682 |
-// consumeExtName consumes extension name or expanded Any type URL and the |
|
| 683 |
-// following ']'. It returns the name or URL consumed. |
|
| 684 |
-func (p *textParser) consumeExtName() (string, error) {
|
|
| 685 |
- tok := p.next() |
|
| 686 |
- if tok.err != nil {
|
|
| 687 |
- return "", tok.err |
|
| 688 |
- } |
|
| 689 |
- |
|
| 690 |
- // If extension name or type url is quoted, it's a single token. |
|
| 691 |
- if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
|
| 692 |
- name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) |
|
| 693 |
- if err != nil {
|
|
| 694 |
- return "", err |
|
| 695 |
- } |
|
| 696 |
- return name, p.consumeToken("]")
|
|
| 697 |
- } |
|
| 698 |
- |
|
| 699 |
- // Consume everything up to "]" |
|
| 700 |
- var parts []string |
|
| 701 |
- for tok.value != "]" {
|
|
| 702 |
- parts = append(parts, tok.value) |
|
| 703 |
- tok = p.next() |
|
| 704 |
- if tok.err != nil {
|
|
| 705 |
- return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
|
| 706 |
- } |
|
| 707 |
- } |
|
| 708 |
- return strings.Join(parts, ""), nil |
|
| 709 |
-} |
|
| 710 |
- |
|
| 711 | 682 |
// consumeOptionalSeparator consumes an optional semicolon or comma. |
| 712 | 683 |
// It is used in readStruct to provide backward compatibility. |
| 713 | 684 |
func (p *textParser) consumeOptionalSeparator() error {
|
| ... | ... |
@@ -189,7 +189,7 @@ func Background() Context {
|
| 189 | 189 |
} |
| 190 | 190 |
|
| 191 | 191 |
// TODO returns a non-nil, empty Context. Code should use context.TODO when |
| 192 |
-// it's unclear which Context to use or it's is not yet available (because the |
|
| 192 |
+// it's unclear which Context to use or it is not yet available (because the |
|
| 193 | 193 |
// surrounding function has not yet been extended to accept a Context |
| 194 | 194 |
// parameter). TODO is recognized by static analysis tools that determine |
| 195 | 195 |
// whether Contexts are propagated correctly in a program. |
| ... | ... |
@@ -210,13 +210,13 @@ type CancelFunc func() |
| 210 | 210 |
// call cancel as soon as the operations running in this Context complete. |
| 211 | 211 |
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
| 212 | 212 |
c := newCancelCtx(parent) |
| 213 |
- propagateCancel(parent, &c) |
|
| 214 |
- return &c, func() { c.cancel(true, Canceled) }
|
|
| 213 |
+ propagateCancel(parent, c) |
|
| 214 |
+ return c, func() { c.cancel(true, Canceled) }
|
|
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 | 217 |
// newCancelCtx returns an initialized cancelCtx. |
| 218 |
-func newCancelCtx(parent Context) cancelCtx {
|
|
| 219 |
- return cancelCtx{
|
|
| 218 |
+func newCancelCtx(parent Context) *cancelCtx {
|
|
| 219 |
+ return &cancelCtx{
|
|
| 220 | 220 |
Context: parent, |
| 221 | 221 |
done: make(chan struct{}),
|
| 222 | 222 |
} |
| ... | ... |
@@ -259,7 +259,7 @@ func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
| 259 | 259 |
case *cancelCtx: |
| 260 | 260 |
return c, true |
| 261 | 261 |
case *timerCtx: |
| 262 |
- return &c.cancelCtx, true |
|
| 262 |
+ return c.cancelCtx, true |
|
| 263 | 263 |
case *valueCtx: |
| 264 | 264 |
parent = c.Context |
| 265 | 265 |
default: |
| ... | ... |
@@ -377,7 +377,7 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
| 377 | 377 |
// implement Done and Err. It implements cancel by stopping its timer then |
| 378 | 378 |
// delegating to cancelCtx.cancel. |
| 379 | 379 |
type timerCtx struct {
|
| 380 |
- cancelCtx |
|
| 380 |
+ *cancelCtx |
|
| 381 | 381 |
timer *time.Timer // Under cancelCtx.mu. |
| 382 | 382 |
|
| 383 | 383 |
deadline time.Time |
| ... | ... |
@@ -14,6 +14,14 @@ import ( |
| 14 | 14 |
"golang.org/x/net/context" |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 |
+func nop() {}
|
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ testHookContextDoneBeforeHeaders = nop |
|
| 21 |
+ testHookDoReturned = nop |
|
| 22 |
+ testHookDidBodyClose = nop |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 17 | 25 |
// Do sends an HTTP request with the provided http.Client and returns an HTTP response. |
| 18 | 26 |
// If the client is nil, http.DefaultClient is used. |
| 19 | 27 |
// If the context is canceled or times out, ctx.Err() will be returned. |
| ... | ... |
@@ -31,18 +39,51 @@ func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Resp |
| 31 | 31 |
} |
| 32 | 32 |
result := make(chan responseAndError, 1) |
| 33 | 33 |
|
| 34 |
+ // Make local copies of test hooks closed over by goroutines below. |
|
| 35 |
+ // Prevents data races in tests. |
|
| 36 |
+ testHookDoReturned := testHookDoReturned |
|
| 37 |
+ testHookDidBodyClose := testHookDidBodyClose |
|
| 38 |
+ |
|
| 34 | 39 |
go func() {
|
| 35 | 40 |
resp, err := client.Do(req) |
| 41 |
+ testHookDoReturned() |
|
| 36 | 42 |
result <- responseAndError{resp, err}
|
| 37 | 43 |
}() |
| 38 | 44 |
|
| 45 |
+ var resp *http.Response |
|
| 46 |
+ |
|
| 39 | 47 |
select {
|
| 40 | 48 |
case <-ctx.Done(): |
| 49 |
+ testHookContextDoneBeforeHeaders() |
|
| 41 | 50 |
cancel() |
| 51 |
+ // Clean up after the goroutine calling client.Do: |
|
| 52 |
+ go func() {
|
|
| 53 |
+ if r := <-result; r.resp != nil {
|
|
| 54 |
+ testHookDidBodyClose() |
|
| 55 |
+ r.resp.Body.Close() |
|
| 56 |
+ } |
|
| 57 |
+ }() |
|
| 42 | 58 |
return nil, ctx.Err() |
| 43 | 59 |
case r := <-result: |
| 44 |
- return r.resp, r.err |
|
| 60 |
+ var err error |
|
| 61 |
+ resp, err = r.resp, r.err |
|
| 62 |
+ if err != nil {
|
|
| 63 |
+ return resp, err |
|
| 64 |
+ } |
|
| 45 | 65 |
} |
| 66 |
+ |
|
| 67 |
+ c := make(chan struct{})
|
|
| 68 |
+ go func() {
|
|
| 69 |
+ select {
|
|
| 70 |
+ case <-ctx.Done(): |
|
| 71 |
+ cancel() |
|
| 72 |
+ case <-c: |
|
| 73 |
+ // The response's Body is closed. |
|
| 74 |
+ } |
|
| 75 |
+ }() |
|
| 76 |
+ resp.Body = ¬ifyingReader{resp.Body, c}
|
|
| 77 |
+ |
|
| 78 |
+ return resp, nil |
|
| 46 | 79 |
} |
| 47 | 80 |
|
| 48 | 81 |
// Get issues a GET request via the Do function. |
| ... | ... |
@@ -77,3 +118,28 @@ func Post(ctx context.Context, client *http.Client, url string, bodyType string, |
| 77 | 77 |
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
|
| 78 | 78 |
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) |
| 79 | 79 |
} |
| 80 |
+ |
|
| 81 |
+// notifyingReader is an io.ReadCloser that closes the notify channel after |
|
| 82 |
+// Close is called or a Read fails on the underlying ReadCloser. |
|
| 83 |
+type notifyingReader struct {
|
|
| 84 |
+ io.ReadCloser |
|
| 85 |
+ notify chan<- struct{}
|
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func (r *notifyingReader) Read(p []byte) (int, error) {
|
|
| 89 |
+ n, err := r.ReadCloser.Read(p) |
|
| 90 |
+ if err != nil && r.notify != nil {
|
|
| 91 |
+ close(r.notify) |
|
| 92 |
+ r.notify = nil |
|
| 93 |
+ } |
|
| 94 |
+ return n, err |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func (r *notifyingReader) Close() error {
|
|
| 98 |
+ err := r.ReadCloser.Close() |
|
| 99 |
+ if r.notify != nil {
|
|
| 100 |
+ close(r.notify) |
|
| 101 |
+ r.notify = nil |
|
| 102 |
+ } |
|
| 103 |
+ return err |
|
| 104 |
+} |
| ... | ... |
@@ -17,8 +17,15 @@ RUN apt-get install -y --no-install-recommends \ |
| 17 | 17 |
libcunit1-dev libssl-dev libxml2-dev libevent-dev \ |
| 18 | 18 |
automake autoconf |
| 19 | 19 |
|
| 20 |
+# The list of packages nghttp2 recommends for h2load: |
|
| 21 |
+RUN apt-get install -y --no-install-recommends make binutils \ |
|
| 22 |
+ autoconf automake autotools-dev \ |
|
| 23 |
+ libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev \ |
|
| 24 |
+ libev-dev libevent-dev libjansson-dev libjemalloc-dev \ |
|
| 25 |
+ cython python3.4-dev python-setuptools |
|
| 26 |
+ |
|
| 20 | 27 |
# Note: setting NGHTTP2_VER before the git clone, so an old git clone isn't cached: |
| 21 |
-ENV NGHTTP2_VER af24f8394e43f4 |
|
| 28 |
+ENV NGHTTP2_VER 895da9a |
|
| 22 | 29 |
RUN cd /root && git clone https://github.com/tatsuhiro-t/nghttp2.git |
| 23 | 30 |
|
| 24 | 31 |
WORKDIR /root/nghttp2 |
| ... | ... |
@@ -31,9 +38,9 @@ RUN make |
| 31 | 31 |
RUN make install |
| 32 | 32 |
|
| 33 | 33 |
WORKDIR /root |
| 34 |
-RUN wget http://curl.haxx.se/download/curl-7.40.0.tar.gz |
|
| 35 |
-RUN tar -zxvf curl-7.40.0.tar.gz |
|
| 36 |
-WORKDIR /root/curl-7.40.0 |
|
| 34 |
+RUN wget http://curl.haxx.se/download/curl-7.45.0.tar.gz |
|
| 35 |
+RUN tar -zxvf curl-7.45.0.tar.gz |
|
| 36 |
+WORKDIR /root/curl-7.45.0 |
|
| 37 | 37 |
RUN ./configure --with-ssl --with-nghttp2=/usr/local |
| 38 | 38 |
RUN make |
| 39 | 39 |
RUN make install |
| 40 | 40 |
deleted file mode 100644 |
| ... | ... |
@@ -1,76 +0,0 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 5 |
- |
|
| 6 |
-package http2 |
|
| 7 |
- |
|
| 8 |
-import ( |
|
| 9 |
- "errors" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-// buffer is an io.ReadWriteCloser backed by a fixed size buffer. |
|
| 13 |
-// It never allocates, but moves old data as new data is written. |
|
| 14 |
-type buffer struct {
|
|
| 15 |
- buf []byte |
|
| 16 |
- r, w int |
|
| 17 |
- closed bool |
|
| 18 |
- err error // err to return to reader |
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 |
-var ( |
|
| 22 |
- errReadEmpty = errors.New("read from empty buffer")
|
|
| 23 |
- errWriteClosed = errors.New("write on closed buffer")
|
|
| 24 |
- errWriteFull = errors.New("write on full buffer")
|
|
| 25 |
-) |
|
| 26 |
- |
|
| 27 |
-// Read copies bytes from the buffer into p. |
|
| 28 |
-// It is an error to read when no data is available. |
|
| 29 |
-func (b *buffer) Read(p []byte) (n int, err error) {
|
|
| 30 |
- n = copy(p, b.buf[b.r:b.w]) |
|
| 31 |
- b.r += n |
|
| 32 |
- if b.closed && b.r == b.w {
|
|
| 33 |
- err = b.err |
|
| 34 |
- } else if b.r == b.w && n == 0 {
|
|
| 35 |
- err = errReadEmpty |
|
| 36 |
- } |
|
| 37 |
- return n, err |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-// Len returns the number of bytes of the unread portion of the buffer. |
|
| 41 |
-func (b *buffer) Len() int {
|
|
| 42 |
- return b.w - b.r |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// Write copies bytes from p into the buffer. |
|
| 46 |
-// It is an error to write more data than the buffer can hold. |
|
| 47 |
-func (b *buffer) Write(p []byte) (n int, err error) {
|
|
| 48 |
- if b.closed {
|
|
| 49 |
- return 0, errWriteClosed |
|
| 50 |
- } |
|
| 51 |
- |
|
| 52 |
- // Slide existing data to beginning. |
|
| 53 |
- if b.r > 0 && len(p) > len(b.buf)-b.w {
|
|
| 54 |
- copy(b.buf, b.buf[b.r:b.w]) |
|
| 55 |
- b.w -= b.r |
|
| 56 |
- b.r = 0 |
|
| 57 |
- } |
|
| 58 |
- |
|
| 59 |
- // Write new data. |
|
| 60 |
- n = copy(b.buf[b.w:], p) |
|
| 61 |
- b.w += n |
|
| 62 |
- if n < len(p) {
|
|
| 63 |
- err = errWriteFull |
|
| 64 |
- } |
|
| 65 |
- return n, err |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-// Close marks the buffer as closed. Future calls to Write will |
|
| 69 |
-// return an error. Future calls to Read, once the buffer is |
|
| 70 |
-// empty, will return err. |
|
| 71 |
-func (b *buffer) Close(err error) {
|
|
| 72 |
- if !b.closed {
|
|
| 73 |
- b.closed = true |
|
| 74 |
- b.err = err |
|
| 75 |
- } |
|
| 76 |
-} |
| 77 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,225 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// Transport code's client connection pooling. |
|
| 5 |
+ |
|
| 6 |
+package http2 |
|
| 7 |
+ |
|
| 8 |
+import ( |
|
| 9 |
+ "crypto/tls" |
|
| 10 |
+ "net/http" |
|
| 11 |
+ "sync" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// ClientConnPool manages a pool of HTTP/2 client connections. |
|
| 15 |
+type ClientConnPool interface {
|
|
| 16 |
+ GetClientConn(req *http.Request, addr string) (*ClientConn, error) |
|
| 17 |
+ MarkDead(*ClientConn) |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+// TODO: use singleflight for dialing and addConnCalls? |
|
| 21 |
+type clientConnPool struct {
|
|
| 22 |
+ t *Transport |
|
| 23 |
+ |
|
| 24 |
+ mu sync.Mutex // TODO: maybe switch to RWMutex |
|
| 25 |
+ // TODO: add support for sharing conns based on cert names |
|
| 26 |
+ // (e.g. share conn for googleapis.com and appspot.com) |
|
| 27 |
+ conns map[string][]*ClientConn // key is host:port |
|
| 28 |
+ dialing map[string]*dialCall // currently in-flight dials |
|
| 29 |
+ keys map[*ClientConn][]string |
|
| 30 |
+ addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
|
| 34 |
+ return p.getClientConn(req, addr, dialOnMiss) |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+const ( |
|
| 38 |
+ dialOnMiss = true |
|
| 39 |
+ noDialOnMiss = false |
|
| 40 |
+) |
|
| 41 |
+ |
|
| 42 |
+func (p *clientConnPool) getClientConn(_ *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
|
| 43 |
+ p.mu.Lock() |
|
| 44 |
+ for _, cc := range p.conns[addr] {
|
|
| 45 |
+ if cc.CanTakeNewRequest() {
|
|
| 46 |
+ p.mu.Unlock() |
|
| 47 |
+ return cc, nil |
|
| 48 |
+ } |
|
| 49 |
+ } |
|
| 50 |
+ if !dialOnMiss {
|
|
| 51 |
+ p.mu.Unlock() |
|
| 52 |
+ return nil, ErrNoCachedConn |
|
| 53 |
+ } |
|
| 54 |
+ call := p.getStartDialLocked(addr) |
|
| 55 |
+ p.mu.Unlock() |
|
| 56 |
+ <-call.done |
|
| 57 |
+ return call.res, call.err |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// dialCall is an in-flight Transport dial call to a host. |
|
| 61 |
+type dialCall struct {
|
|
| 62 |
+ p *clientConnPool |
|
| 63 |
+ done chan struct{} // closed when done
|
|
| 64 |
+ res *ClientConn // valid after done is closed |
|
| 65 |
+ err error // valid after done is closed |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// requires p.mu is held. |
|
| 69 |
+func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
|
|
| 70 |
+ if call, ok := p.dialing[addr]; ok {
|
|
| 71 |
+ // A dial is already in-flight. Don't start another. |
|
| 72 |
+ return call |
|
| 73 |
+ } |
|
| 74 |
+ call := &dialCall{p: p, done: make(chan struct{})}
|
|
| 75 |
+ if p.dialing == nil {
|
|
| 76 |
+ p.dialing = make(map[string]*dialCall) |
|
| 77 |
+ } |
|
| 78 |
+ p.dialing[addr] = call |
|
| 79 |
+ go call.dial(addr) |
|
| 80 |
+ return call |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// run in its own goroutine. |
|
| 84 |
+func (c *dialCall) dial(addr string) {
|
|
| 85 |
+ c.res, c.err = c.p.t.dialClientConn(addr) |
|
| 86 |
+ close(c.done) |
|
| 87 |
+ |
|
| 88 |
+ c.p.mu.Lock() |
|
| 89 |
+ delete(c.p.dialing, addr) |
|
| 90 |
+ if c.err == nil {
|
|
| 91 |
+ c.p.addConnLocked(addr, c.res) |
|
| 92 |
+ } |
|
| 93 |
+ c.p.mu.Unlock() |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't |
|
| 97 |
+// already exist. It coalesces concurrent calls with the same key. |
|
| 98 |
+// This is used by the http1 Transport code when it creates a new connection. Because |
|
| 99 |
+// the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know |
|
| 100 |
+// the protocol), it can get into a situation where it has multiple TLS connections. |
|
| 101 |
+// This code decides which ones live or die. |
|
| 102 |
+// The return value used is whether c was used. |
|
| 103 |
+// c is never closed. |
|
| 104 |
+func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
|
|
| 105 |
+ p.mu.Lock() |
|
| 106 |
+ for _, cc := range p.conns[key] {
|
|
| 107 |
+ if cc.CanTakeNewRequest() {
|
|
| 108 |
+ p.mu.Unlock() |
|
| 109 |
+ return false, nil |
|
| 110 |
+ } |
|
| 111 |
+ } |
|
| 112 |
+ call, dup := p.addConnCalls[key] |
|
| 113 |
+ if !dup {
|
|
| 114 |
+ if p.addConnCalls == nil {
|
|
| 115 |
+ p.addConnCalls = make(map[string]*addConnCall) |
|
| 116 |
+ } |
|
| 117 |
+ call = &addConnCall{
|
|
| 118 |
+ p: p, |
|
| 119 |
+ done: make(chan struct{}),
|
|
| 120 |
+ } |
|
| 121 |
+ p.addConnCalls[key] = call |
|
| 122 |
+ go call.run(t, key, c) |
|
| 123 |
+ } |
|
| 124 |
+ p.mu.Unlock() |
|
| 125 |
+ |
|
| 126 |
+ <-call.done |
|
| 127 |
+ if call.err != nil {
|
|
| 128 |
+ return false, call.err |
|
| 129 |
+ } |
|
| 130 |
+ return !dup, nil |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+type addConnCall struct {
|
|
| 134 |
+ p *clientConnPool |
|
| 135 |
+ done chan struct{} // closed when done
|
|
| 136 |
+ err error |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
|
| 140 |
+ cc, err := t.NewClientConn(tc) |
|
| 141 |
+ |
|
| 142 |
+ p := c.p |
|
| 143 |
+ p.mu.Lock() |
|
| 144 |
+ if err != nil {
|
|
| 145 |
+ c.err = err |
|
| 146 |
+ } else {
|
|
| 147 |
+ p.addConnLocked(key, cc) |
|
| 148 |
+ } |
|
| 149 |
+ delete(p.addConnCalls, key) |
|
| 150 |
+ p.mu.Unlock() |
|
| 151 |
+ close(c.done) |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func (p *clientConnPool) addConn(key string, cc *ClientConn) {
|
|
| 155 |
+ p.mu.Lock() |
|
| 156 |
+ p.addConnLocked(key, cc) |
|
| 157 |
+ p.mu.Unlock() |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+// p.mu must be held |
|
| 161 |
+func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
|
| 162 |
+ for _, v := range p.conns[key] {
|
|
| 163 |
+ if v == cc {
|
|
| 164 |
+ return |
|
| 165 |
+ } |
|
| 166 |
+ } |
|
| 167 |
+ if p.conns == nil {
|
|
| 168 |
+ p.conns = make(map[string][]*ClientConn) |
|
| 169 |
+ } |
|
| 170 |
+ if p.keys == nil {
|
|
| 171 |
+ p.keys = make(map[*ClientConn][]string) |
|
| 172 |
+ } |
|
| 173 |
+ p.conns[key] = append(p.conns[key], cc) |
|
| 174 |
+ p.keys[cc] = append(p.keys[cc], key) |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func (p *clientConnPool) MarkDead(cc *ClientConn) {
|
|
| 178 |
+ p.mu.Lock() |
|
| 179 |
+ defer p.mu.Unlock() |
|
| 180 |
+ for _, key := range p.keys[cc] {
|
|
| 181 |
+ vv, ok := p.conns[key] |
|
| 182 |
+ if !ok {
|
|
| 183 |
+ continue |
|
| 184 |
+ } |
|
| 185 |
+ newList := filterOutClientConn(vv, cc) |
|
| 186 |
+ if len(newList) > 0 {
|
|
| 187 |
+ p.conns[key] = newList |
|
| 188 |
+ } else {
|
|
| 189 |
+ delete(p.conns, key) |
|
| 190 |
+ } |
|
| 191 |
+ } |
|
| 192 |
+ delete(p.keys, cc) |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 195 |
+func (p *clientConnPool) closeIdleConnections() {
|
|
| 196 |
+ p.mu.Lock() |
|
| 197 |
+ defer p.mu.Unlock() |
|
| 198 |
+ // TODO: don't close a cc if it was just added to the pool |
|
| 199 |
+ // milliseconds ago and has never been used. There's currently |
|
| 200 |
+ // a small race window with the HTTP/1 Transport's integration |
|
| 201 |
+ // where it can add an idle conn just before using it, and |
|
| 202 |
+ // somebody else can concurrently call CloseIdleConns and |
|
| 203 |
+ // break some caller's RoundTrip. |
|
| 204 |
+ for _, vv := range p.conns {
|
|
| 205 |
+ for _, cc := range vv {
|
|
| 206 |
+ cc.closeIfIdle() |
|
| 207 |
+ } |
|
| 208 |
+ } |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
|
|
| 212 |
+ out := in[:0] |
|
| 213 |
+ for _, v := range in {
|
|
| 214 |
+ if v != exclude {
|
|
| 215 |
+ out = append(out, v) |
|
| 216 |
+ } |
|
| 217 |
+ } |
|
| 218 |
+ // If we filtered it out, zero out the last item to prevent |
|
| 219 |
+ // the GC from seeing it. |
|
| 220 |
+ if len(in) != len(out) {
|
|
| 221 |
+ in[len(in)-1] = nil |
|
| 222 |
+ } |
|
| 223 |
+ return out |
|
| 224 |
+} |
| 0 | 225 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,89 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build go1.6 |
|
| 5 |
+ |
|
| 6 |
+package http2 |
|
| 7 |
+ |
|
| 8 |
+import ( |
|
| 9 |
+ "crypto/tls" |
|
| 10 |
+ "fmt" |
|
| 11 |
+ "net/http" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+func configureTransport(t1 *http.Transport) (*Transport, error) {
|
|
| 15 |
+ connPool := new(clientConnPool) |
|
| 16 |
+ t2 := &Transport{
|
|
| 17 |
+ ConnPool: noDialClientConnPool{connPool},
|
|
| 18 |
+ t1: t1, |
|
| 19 |
+ } |
|
| 20 |
+ connPool.t = t2 |
|
| 21 |
+ if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
|
|
| 22 |
+ return nil, err |
|
| 23 |
+ } |
|
| 24 |
+ if t1.TLSClientConfig == nil {
|
|
| 25 |
+ t1.TLSClientConfig = new(tls.Config) |
|
| 26 |
+ } |
|
| 27 |
+ if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
|
|
| 28 |
+ t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
|
|
| 29 |
+ } |
|
| 30 |
+ if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
|
|
| 31 |
+ t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1") |
|
| 32 |
+ } |
|
| 33 |
+ upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
|
|
| 34 |
+ addr := authorityAddr(authority) |
|
| 35 |
+ if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
|
|
| 36 |
+ go c.Close() |
|
| 37 |
+ return erringRoundTripper{err}
|
|
| 38 |
+ } else if !used {
|
|
| 39 |
+ // Turns out we don't need this c. |
|
| 40 |
+ // For example, two goroutines made requests to the same host |
|
| 41 |
+ // at the same time, both kicking off TCP dials. (since protocol |
|
| 42 |
+ // was unknown) |
|
| 43 |
+ go c.Close() |
|
| 44 |
+ } |
|
| 45 |
+ return t2 |
|
| 46 |
+ } |
|
| 47 |
+ if m := t1.TLSNextProto; len(m) == 0 {
|
|
| 48 |
+ t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
|
|
| 49 |
+ "h2": upgradeFn, |
|
| 50 |
+ } |
|
| 51 |
+ } else {
|
|
| 52 |
+ m["h2"] = upgradeFn |
|
| 53 |
+ } |
|
| 54 |
+ return t2, nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+// registerHTTPSProtocol calls Transport.RegisterProtocol but |
|
| 58 |
+// convering panics into errors. |
|
| 59 |
+func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
|
|
| 60 |
+ defer func() {
|
|
| 61 |
+ if e := recover(); e != nil {
|
|
| 62 |
+ err = fmt.Errorf("%v", e)
|
|
| 63 |
+ } |
|
| 64 |
+ }() |
|
| 65 |
+ t.RegisterProtocol("https", rt)
|
|
| 66 |
+ return nil |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// noDialClientConnPool is an implementation of http2.ClientConnPool |
|
| 70 |
+// which never dials. We let the HTTP/1.1 client dial and use its TLS |
|
| 71 |
+// connection instead. |
|
| 72 |
+type noDialClientConnPool struct{ *clientConnPool }
|
|
| 73 |
+ |
|
| 74 |
+func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
|
| 75 |
+ return p.getClientConn(req, addr, noDialOnMiss) |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// noDialH2RoundTripper is a RoundTripper which only tries to complete the request |
|
| 79 |
+// if there's already has a cached connection to the host. |
|
| 80 |
+type noDialH2RoundTripper struct{ t *Transport }
|
|
| 81 |
+ |
|
| 82 |
+func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
| 83 |
+ res, err := rt.t.RoundTrip(req) |
|
| 84 |
+ if err == ErrNoCachedConn {
|
|
| 85 |
+ return nil, http.ErrSkipAltProtocol |
|
| 86 |
+ } |
|
| 87 |
+ return res, err |
|
| 88 |
+} |
| ... | ... |
@@ -1,11 +1,13 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
package http2 |
| 7 | 6 |
|
| 8 |
-import "fmt" |
|
| 7 |
+import ( |
|
| 8 |
+ "errors" |
|
| 9 |
+ "fmt" |
|
| 10 |
+) |
|
| 9 | 11 |
|
| 10 | 12 |
// An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec. |
| 11 | 13 |
type ErrCode uint32 |
| ... | ... |
@@ -76,3 +78,45 @@ func (e StreamError) Error() string {
|
| 76 | 76 |
type goAwayFlowError struct{}
|
| 77 | 77 |
|
| 78 | 78 |
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
|
| 79 |
+ |
|
| 80 |
+// connErrorReason wraps a ConnectionError with an informative error about why it occurs. |
|
| 81 |
+ |
|
| 82 |
+// Errors of this type are only returned by the frame parser functions |
|
| 83 |
+// and converted into ConnectionError(ErrCodeProtocol). |
|
| 84 |
+type connError struct {
|
|
| 85 |
+ Code ErrCode |
|
| 86 |
+ Reason string |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+func (e connError) Error() string {
|
|
| 90 |
+ return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason)
|
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+type pseudoHeaderError string |
|
| 94 |
+ |
|
| 95 |
+func (e pseudoHeaderError) Error() string {
|
|
| 96 |
+ return fmt.Sprintf("invalid pseudo-header %q", string(e))
|
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+type duplicatePseudoHeaderError string |
|
| 100 |
+ |
|
| 101 |
+func (e duplicatePseudoHeaderError) Error() string {
|
|
| 102 |
+ return fmt.Sprintf("duplicate pseudo-header %q", string(e))
|
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+type headerFieldNameError string |
|
| 106 |
+ |
|
| 107 |
+func (e headerFieldNameError) Error() string {
|
|
| 108 |
+ return fmt.Sprintf("invalid header field name %q", string(e))
|
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+type headerFieldValueError string |
|
| 112 |
+ |
|
| 113 |
+func (e headerFieldValueError) Error() string {
|
|
| 114 |
+ return fmt.Sprintf("invalid header field value %q", string(e))
|
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+var ( |
|
| 118 |
+ errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers")
|
|
| 119 |
+ errPseudoAfterRegular = errors.New("pseudo header field after regular")
|
|
| 120 |
+) |
| 79 | 121 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,60 @@ |
| 0 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+package http2 |
|
| 5 |
+ |
|
| 6 |
+import ( |
|
| 7 |
+ "errors" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// fixedBuffer is an io.ReadWriter backed by a fixed size buffer. |
|
| 11 |
+// It never allocates, but moves old data as new data is written. |
|
| 12 |
+type fixedBuffer struct {
|
|
| 13 |
+ buf []byte |
|
| 14 |
+ r, w int |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+var ( |
|
| 18 |
+ errReadEmpty = errors.New("read from empty fixedBuffer")
|
|
| 19 |
+ errWriteFull = errors.New("write on full fixedBuffer")
|
|
| 20 |
+) |
|
| 21 |
+ |
|
| 22 |
+// Read copies bytes from the buffer into p. |
|
| 23 |
+// It is an error to read when no data is available. |
|
| 24 |
+func (b *fixedBuffer) Read(p []byte) (n int, err error) {
|
|
| 25 |
+ if b.r == b.w {
|
|
| 26 |
+ return 0, errReadEmpty |
|
| 27 |
+ } |
|
| 28 |
+ n = copy(p, b.buf[b.r:b.w]) |
|
| 29 |
+ b.r += n |
|
| 30 |
+ if b.r == b.w {
|
|
| 31 |
+ b.r = 0 |
|
| 32 |
+ b.w = 0 |
|
| 33 |
+ } |
|
| 34 |
+ return n, nil |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// Len returns the number of bytes of the unread portion of the buffer. |
|
| 38 |
+func (b *fixedBuffer) Len() int {
|
|
| 39 |
+ return b.w - b.r |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// Write copies bytes from p into the buffer. |
|
| 43 |
+// It is an error to write more data than the buffer can hold. |
|
| 44 |
+func (b *fixedBuffer) Write(p []byte) (n int, err error) {
|
|
| 45 |
+ // Slide existing data to beginning. |
|
| 46 |
+ if b.r > 0 && len(p) > len(b.buf)-b.w {
|
|
| 47 |
+ copy(b.buf, b.buf[b.r:b.w]) |
|
| 48 |
+ b.w -= b.r |
|
| 49 |
+ b.r = 0 |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ // Write new data. |
|
| 53 |
+ n = copy(b.buf[b.w:], p) |
|
| 54 |
+ b.w += n |
|
| 55 |
+ if n < len(p) {
|
|
| 56 |
+ err = errWriteFull |
|
| 57 |
+ } |
|
| 58 |
+ return n, err |
|
| 59 |
+} |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
// Flow control |
| 7 | 6 |
|
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
package http2 |
| 7 | 6 |
|
| ... | ... |
@@ -11,7 +10,11 @@ import ( |
| 11 | 11 |
"errors" |
| 12 | 12 |
"fmt" |
| 13 | 13 |
"io" |
| 14 |
+ "log" |
|
| 15 |
+ "strings" |
|
| 14 | 16 |
"sync" |
| 17 |
+ |
|
| 18 |
+ "golang.org/x/net/http2/hpack" |
|
| 15 | 19 |
) |
| 16 | 20 |
|
| 17 | 21 |
const frameHeaderLen = 9 |
| ... | ... |
@@ -172,6 +175,12 @@ func (h FrameHeader) Header() FrameHeader { return h }
|
| 172 | 172 |
func (h FrameHeader) String() string {
|
| 173 | 173 |
var buf bytes.Buffer |
| 174 | 174 |
buf.WriteString("[FrameHeader ")
|
| 175 |
+ h.writeDebug(&buf) |
|
| 176 |
+ buf.WriteByte(']')
|
|
| 177 |
+ return buf.String() |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+func (h FrameHeader) writeDebug(buf *bytes.Buffer) {
|
|
| 175 | 181 |
buf.WriteString(h.Type.String()) |
| 176 | 182 |
if h.Flags != 0 {
|
| 177 | 183 |
buf.WriteString(" flags=")
|
| ... | ... |
@@ -188,15 +197,14 @@ func (h FrameHeader) String() string {
|
| 188 | 188 |
if name != "" {
|
| 189 | 189 |
buf.WriteString(name) |
| 190 | 190 |
} else {
|
| 191 |
- fmt.Fprintf(&buf, "0x%x", 1<<i) |
|
| 191 |
+ fmt.Fprintf(buf, "0x%x", 1<<i) |
|
| 192 | 192 |
} |
| 193 | 193 |
} |
| 194 | 194 |
} |
| 195 | 195 |
if h.StreamID != 0 {
|
| 196 |
- fmt.Fprintf(&buf, " stream=%d", h.StreamID) |
|
| 196 |
+ fmt.Fprintf(buf, " stream=%d", h.StreamID) |
|
| 197 | 197 |
} |
| 198 |
- fmt.Fprintf(&buf, " len=%d]", h.Length) |
|
| 199 |
- return buf.String() |
|
| 198 |
+ fmt.Fprintf(buf, " len=%d", h.Length) |
|
| 200 | 199 |
} |
| 201 | 200 |
|
| 202 | 201 |
func (h *FrameHeader) checkValid() {
|
| ... | ... |
@@ -256,6 +264,11 @@ type Frame interface {
|
| 256 | 256 |
type Framer struct {
|
| 257 | 257 |
r io.Reader |
| 258 | 258 |
lastFrame Frame |
| 259 |
+ errDetail error |
|
| 260 |
+ |
|
| 261 |
+ // lastHeaderStream is non-zero if the last frame was an |
|
| 262 |
+ // unfinished HEADERS/CONTINUATION. |
|
| 263 |
+ lastHeaderStream uint32 |
|
| 259 | 264 |
|
| 260 | 265 |
maxReadSize uint32 |
| 261 | 266 |
headerBuf [frameHeaderLen]byte |
| ... | ... |
@@ -272,18 +285,48 @@ type Framer struct {
|
| 272 | 272 |
wbuf []byte |
| 273 | 273 |
|
| 274 | 274 |
// AllowIllegalWrites permits the Framer's Write methods to |
| 275 |
- // write frames that do not conform to the HTTP/2 spec. This |
|
| 275 |
+ // write frames that do not conform to the HTTP/2 spec. This |
|
| 276 | 276 |
// permits using the Framer to test other HTTP/2 |
| 277 | 277 |
// implementations' conformance to the spec. |
| 278 | 278 |
// If false, the Write methods will prefer to return an error |
| 279 | 279 |
// rather than comply. |
| 280 | 280 |
AllowIllegalWrites bool |
| 281 | 281 |
|
| 282 |
+ // AllowIllegalReads permits the Framer's ReadFrame method |
|
| 283 |
+ // to return non-compliant frames or frame orders. |
|
| 284 |
+ // This is for testing and permits using the Framer to test |
|
| 285 |
+ // other HTTP/2 implementations' conformance to the spec. |
|
| 286 |
+ // It is not compatible with ReadMetaHeaders. |
|
| 287 |
+ AllowIllegalReads bool |
|
| 288 |
+ |
|
| 289 |
+ // ReadMetaHeaders if non-nil causes ReadFrame to merge |
|
| 290 |
+ // HEADERS and CONTINUATION frames together and return |
|
| 291 |
+ // MetaHeadersFrame instead. |
|
| 292 |
+ ReadMetaHeaders *hpack.Decoder |
|
| 293 |
+ |
|
| 294 |
+ // MaxHeaderListSize is the http2 MAX_HEADER_LIST_SIZE. |
|
| 295 |
+ // It's used only if ReadMetaHeaders is set; 0 means a sane default |
|
| 296 |
+ // (currently 16MB) |
|
| 297 |
+ // If the limit is hit, MetaHeadersFrame.Truncated is set true. |
|
| 298 |
+ MaxHeaderListSize uint32 |
|
| 299 |
+ |
|
| 282 | 300 |
// TODO: track which type of frame & with which flags was sent |
| 283 | 301 |
// last. Then return an error (unless AllowIllegalWrites) if |
| 284 | 302 |
// we're in the middle of a header block and a |
| 285 | 303 |
// non-Continuation or Continuation on a different stream is |
| 286 | 304 |
// attempted to be written. |
| 305 |
+ |
|
| 306 |
+ logReads bool |
|
| 307 |
+ |
|
| 308 |
+ debugFramer *Framer // only use for logging written writes |
|
| 309 |
+ debugFramerBuf *bytes.Buffer |
|
| 310 |
+} |
|
| 311 |
+ |
|
| 312 |
+func (fr *Framer) maxHeaderListSize() uint32 {
|
|
| 313 |
+ if fr.MaxHeaderListSize == 0 {
|
|
| 314 |
+ return 16 << 20 // sane default, per docs |
|
| 315 |
+ } |
|
| 316 |
+ return fr.MaxHeaderListSize |
|
| 287 | 317 |
} |
| 288 | 318 |
|
| 289 | 319 |
func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) {
|
| ... | ... |
@@ -311,6 +354,10 @@ func (f *Framer) endWrite() error {
|
| 311 | 311 |
byte(length>>16), |
| 312 | 312 |
byte(length>>8), |
| 313 | 313 |
byte(length)) |
| 314 |
+ if logFrameWrites {
|
|
| 315 |
+ f.logWrite() |
|
| 316 |
+ } |
|
| 317 |
+ |
|
| 314 | 318 |
n, err := f.w.Write(f.wbuf) |
| 315 | 319 |
if err == nil && n != len(f.wbuf) {
|
| 316 | 320 |
err = io.ErrShortWrite |
| ... | ... |
@@ -318,6 +365,24 @@ func (f *Framer) endWrite() error {
|
| 318 | 318 |
return err |
| 319 | 319 |
} |
| 320 | 320 |
|
| 321 |
+func (f *Framer) logWrite() {
|
|
| 322 |
+ if f.debugFramer == nil {
|
|
| 323 |
+ f.debugFramerBuf = new(bytes.Buffer) |
|
| 324 |
+ f.debugFramer = NewFramer(nil, f.debugFramerBuf) |
|
| 325 |
+ f.debugFramer.logReads = false // we log it ourselves, saying "wrote" below |
|
| 326 |
+ // Let us read anything, even if we accidentally wrote it |
|
| 327 |
+ // in the wrong order: |
|
| 328 |
+ f.debugFramer.AllowIllegalReads = true |
|
| 329 |
+ } |
|
| 330 |
+ f.debugFramerBuf.Write(f.wbuf) |
|
| 331 |
+ fr, err := f.debugFramer.ReadFrame() |
|
| 332 |
+ if err != nil {
|
|
| 333 |
+ log.Printf("http2: Framer %p: failed to decode just-written frame", f)
|
|
| 334 |
+ return |
|
| 335 |
+ } |
|
| 336 |
+ log.Printf("http2: Framer %p: wrote %v", f, summarizeFrame(fr))
|
|
| 337 |
+} |
|
| 338 |
+ |
|
| 321 | 339 |
func (f *Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) }
|
| 322 | 340 |
func (f *Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) }
|
| 323 | 341 |
func (f *Framer) writeUint16(v uint16) { f.wbuf = append(f.wbuf, byte(v>>8), byte(v)) }
|
| ... | ... |
@@ -333,8 +398,9 @@ const ( |
| 333 | 333 |
// NewFramer returns a Framer that writes frames to w and reads them from r. |
| 334 | 334 |
func NewFramer(w io.Writer, r io.Reader) *Framer {
|
| 335 | 335 |
fr := &Framer{
|
| 336 |
- w: w, |
|
| 337 |
- r: r, |
|
| 336 |
+ w: w, |
|
| 337 |
+ r: r, |
|
| 338 |
+ logReads: logFrameReads, |
|
| 338 | 339 |
} |
| 339 | 340 |
fr.getReadBuf = func(size uint32) []byte {
|
| 340 | 341 |
if cap(fr.readBuf) >= int(size) {
|
| ... | ... |
@@ -358,15 +424,39 @@ func (fr *Framer) SetMaxReadFrameSize(v uint32) {
|
| 358 | 358 |
fr.maxReadSize = v |
| 359 | 359 |
} |
| 360 | 360 |
|
| 361 |
+// ErrorDetail returns a more detailed error of the last error |
|
| 362 |
+// returned by Framer.ReadFrame. For instance, if ReadFrame |
|
| 363 |
+// returns a StreamError with code PROTOCOL_ERROR, ErrorDetail |
|
| 364 |
+// will say exactly what was invalid. ErrorDetail is not guaranteed |
|
| 365 |
+// to return a non-nil value and like the rest of the http2 package, |
|
| 366 |
+// its return value is not protected by an API compatibility promise. |
|
| 367 |
+// ErrorDetail is reset after the next call to ReadFrame. |
|
| 368 |
+func (fr *Framer) ErrorDetail() error {
|
|
| 369 |
+ return fr.errDetail |
|
| 370 |
+} |
|
| 371 |
+ |
|
| 361 | 372 |
// ErrFrameTooLarge is returned from Framer.ReadFrame when the peer |
| 362 | 373 |
// sends a frame that is larger than declared with SetMaxReadFrameSize. |
| 363 | 374 |
var ErrFrameTooLarge = errors.New("http2: frame too large")
|
| 364 | 375 |
|
| 376 |
+// terminalReadFrameError reports whether err is an unrecoverable |
|
| 377 |
+// error from ReadFrame and no other frames should be read. |
|
| 378 |
+func terminalReadFrameError(err error) bool {
|
|
| 379 |
+ if _, ok := err.(StreamError); ok {
|
|
| 380 |
+ return false |
|
| 381 |
+ } |
|
| 382 |
+ return err != nil |
|
| 383 |
+} |
|
| 384 |
+ |
|
| 365 | 385 |
// ReadFrame reads a single frame. The returned Frame is only valid |
| 366 | 386 |
// until the next call to ReadFrame. |
| 367 |
-// If the frame is larger than previously set with SetMaxReadFrameSize, |
|
| 368 |
-// the returned error is ErrFrameTooLarge. |
|
| 387 |
+// |
|
| 388 |
+// If the frame is larger than previously set with SetMaxReadFrameSize, the |
|
| 389 |
+// returned error is ErrFrameTooLarge. Other errors may be of type |
|
| 390 |
+// ConnectionError, StreamError, or anything else from from the underlying |
|
| 391 |
+// reader. |
|
| 369 | 392 |
func (fr *Framer) ReadFrame() (Frame, error) {
|
| 393 |
+ fr.errDetail = nil |
|
| 370 | 394 |
if fr.lastFrame != nil {
|
| 371 | 395 |
fr.lastFrame.invalidate() |
| 372 | 396 |
} |
| ... | ... |
@@ -383,12 +473,71 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
| 383 | 383 |
} |
| 384 | 384 |
f, err := typeFrameParser(fh.Type)(fh, payload) |
| 385 | 385 |
if err != nil {
|
| 386 |
+ if ce, ok := err.(connError); ok {
|
|
| 387 |
+ return nil, fr.connError(ce.Code, ce.Reason) |
|
| 388 |
+ } |
|
| 386 | 389 |
return nil, err |
| 387 | 390 |
} |
| 388 |
- fr.lastFrame = f |
|
| 391 |
+ if err := fr.checkFrameOrder(f); err != nil {
|
|
| 392 |
+ return nil, err |
|
| 393 |
+ } |
|
| 394 |
+ if fr.logReads {
|
|
| 395 |
+ log.Printf("http2: Framer %p: read %v", fr, summarizeFrame(f))
|
|
| 396 |
+ } |
|
| 397 |
+ if fh.Type == FrameHeaders && fr.ReadMetaHeaders != nil {
|
|
| 398 |
+ return fr.readMetaFrame(f.(*HeadersFrame)) |
|
| 399 |
+ } |
|
| 389 | 400 |
return f, nil |
| 390 | 401 |
} |
| 391 | 402 |
|
| 403 |
+// connError returns ConnectionError(code) but first |
|
| 404 |
+// stashes away a public reason to the caller can optionally relay it |
|
| 405 |
+// to the peer before hanging up on them. This might help others debug |
|
| 406 |
+// their implementations. |
|
| 407 |
+func (fr *Framer) connError(code ErrCode, reason string) error {
|
|
| 408 |
+ fr.errDetail = errors.New(reason) |
|
| 409 |
+ return ConnectionError(code) |
|
| 410 |
+} |
|
| 411 |
+ |
|
| 412 |
+// checkFrameOrder reports an error if f is an invalid frame to return |
|
| 413 |
+// next from ReadFrame. Mostly it checks whether HEADERS and |
|
| 414 |
+// CONTINUATION frames are contiguous. |
|
| 415 |
+func (fr *Framer) checkFrameOrder(f Frame) error {
|
|
| 416 |
+ last := fr.lastFrame |
|
| 417 |
+ fr.lastFrame = f |
|
| 418 |
+ if fr.AllowIllegalReads {
|
|
| 419 |
+ return nil |
|
| 420 |
+ } |
|
| 421 |
+ |
|
| 422 |
+ fh := f.Header() |
|
| 423 |
+ if fr.lastHeaderStream != 0 {
|
|
| 424 |
+ if fh.Type != FrameContinuation {
|
|
| 425 |
+ return fr.connError(ErrCodeProtocol, |
|
| 426 |
+ fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d",
|
|
| 427 |
+ fh.Type, fh.StreamID, |
|
| 428 |
+ last.Header().Type, fr.lastHeaderStream)) |
|
| 429 |
+ } |
|
| 430 |
+ if fh.StreamID != fr.lastHeaderStream {
|
|
| 431 |
+ return fr.connError(ErrCodeProtocol, |
|
| 432 |
+ fmt.Sprintf("got CONTINUATION for stream %d; expected stream %d",
|
|
| 433 |
+ fh.StreamID, fr.lastHeaderStream)) |
|
| 434 |
+ } |
|
| 435 |
+ } else if fh.Type == FrameContinuation {
|
|
| 436 |
+ return fr.connError(ErrCodeProtocol, fmt.Sprintf("unexpected CONTINUATION for stream %d", fh.StreamID))
|
|
| 437 |
+ } |
|
| 438 |
+ |
|
| 439 |
+ switch fh.Type {
|
|
| 440 |
+ case FrameHeaders, FrameContinuation: |
|
| 441 |
+ if fh.Flags.Has(FlagHeadersEndHeaders) {
|
|
| 442 |
+ fr.lastHeaderStream = 0 |
|
| 443 |
+ } else {
|
|
| 444 |
+ fr.lastHeaderStream = fh.StreamID |
|
| 445 |
+ } |
|
| 446 |
+ } |
|
| 447 |
+ |
|
| 448 |
+ return nil |
|
| 449 |
+} |
|
| 450 |
+ |
|
| 392 | 451 |
// A DataFrame conveys arbitrary, variable-length sequences of octets |
| 393 | 452 |
// associated with a stream. |
| 394 | 453 |
// See http://http2.github.io/http2-spec/#rfc.section.6.1 |
| ... | ... |
@@ -417,7 +566,7 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
| 417 | 417 |
// field is 0x0, the recipient MUST respond with a |
| 418 | 418 |
// connection error (Section 5.4.1) of type |
| 419 | 419 |
// PROTOCOL_ERROR. |
| 420 |
- return nil, ConnectionError(ErrCodeProtocol) |
|
| 420 |
+ return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
|
|
| 421 | 421 |
} |
| 422 | 422 |
f := &DataFrame{
|
| 423 | 423 |
FrameHeader: fh, |
| ... | ... |
@@ -435,7 +584,7 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
| 435 | 435 |
// length of the frame payload, the recipient MUST |
| 436 | 436 |
// treat this as a connection error. |
| 437 | 437 |
// Filed: https://github.com/http2/http2-spec/issues/610 |
| 438 |
- return nil, ConnectionError(ErrCodeProtocol) |
|
| 438 |
+ return nil, connError{ErrCodeProtocol, "pad size larger than data payload"}
|
|
| 439 | 439 |
} |
| 440 | 440 |
f.data = payload[:len(payload)-int(padSize)] |
| 441 | 441 |
return f, nil |
| ... | ... |
@@ -575,6 +724,8 @@ type PingFrame struct {
|
| 575 | 575 |
Data [8]byte |
| 576 | 576 |
} |
| 577 | 577 |
|
| 578 |
+func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) }
|
|
| 579 |
+ |
|
| 578 | 580 |
func parsePingFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
| 579 | 581 |
if len(payload) != 8 {
|
| 580 | 582 |
return nil, ConnectionError(ErrCodeFrameSize) |
| ... | ... |
@@ -663,7 +814,7 @@ func parseUnknownFrame(fh FrameHeader, p []byte) (Frame, error) {
|
| 663 | 663 |
// See http://http2.github.io/http2-spec/#rfc.section.6.9 |
| 664 | 664 |
type WindowUpdateFrame struct {
|
| 665 | 665 |
FrameHeader |
| 666 |
- Increment uint32 |
|
| 666 |
+ Increment uint32 // never read with high bit set |
|
| 667 | 667 |
} |
| 668 | 668 |
|
| 669 | 669 |
func parseWindowUpdateFrame(fh FrameHeader, p []byte) (Frame, error) {
|
| ... | ... |
@@ -740,7 +891,7 @@ func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
|
| 740 | 740 |
// is received whose stream identifier field is 0x0, the recipient MUST |
| 741 | 741 |
// respond with a connection error (Section 5.4.1) of type |
| 742 | 742 |
// PROTOCOL_ERROR. |
| 743 |
- return nil, ConnectionError(ErrCodeProtocol) |
|
| 743 |
+ return nil, connError{ErrCodeProtocol, "HEADERS frame with stream ID 0"}
|
|
| 744 | 744 |
} |
| 745 | 745 |
var padLength uint8 |
| 746 | 746 |
if fh.Flags.Has(FlagHeadersPadded) {
|
| ... | ... |
@@ -870,10 +1021,10 @@ func (p PriorityParam) IsZero() bool {
|
| 870 | 870 |
|
| 871 | 871 |
func parsePriorityFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
| 872 | 872 |
if fh.StreamID == 0 {
|
| 873 |
- return nil, ConnectionError(ErrCodeProtocol) |
|
| 873 |
+ return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
|
|
| 874 | 874 |
} |
| 875 | 875 |
if len(payload) != 5 {
|
| 876 |
- return nil, ConnectionError(ErrCodeFrameSize) |
|
| 876 |
+ return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))}
|
|
| 877 | 877 |
} |
| 878 | 878 |
v := binary.BigEndian.Uint32(payload[:4]) |
| 879 | 879 |
streamID := v & 0x7fffffff // mask off high bit |
| ... | ... |
@@ -943,13 +1094,12 @@ type ContinuationFrame struct {
|
| 943 | 943 |
} |
| 944 | 944 |
|
| 945 | 945 |
func parseContinuationFrame(fh FrameHeader, p []byte) (Frame, error) {
|
| 946 |
+ if fh.StreamID == 0 {
|
|
| 947 |
+ return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
|
|
| 948 |
+ } |
|
| 946 | 949 |
return &ContinuationFrame{fh, p}, nil
|
| 947 | 950 |
} |
| 948 | 951 |
|
| 949 |
-func (f *ContinuationFrame) StreamEnded() bool {
|
|
| 950 |
- return f.FrameHeader.Flags.Has(FlagDataEndStream) |
|
| 951 |
-} |
|
| 952 |
- |
|
| 953 | 952 |
func (f *ContinuationFrame) HeaderBlockFragment() []byte {
|
| 954 | 953 |
f.checkValid() |
| 955 | 954 |
return f.headerFragBuf |
| ... | ... |
@@ -1111,3 +1261,236 @@ type streamEnder interface {
|
| 1111 | 1111 |
type headersEnder interface {
|
| 1112 | 1112 |
HeadersEnded() bool |
| 1113 | 1113 |
} |
| 1114 |
+ |
|
| 1115 |
+type headersOrContinuation interface {
|
|
| 1116 |
+ headersEnder |
|
| 1117 |
+ HeaderBlockFragment() []byte |
|
| 1118 |
+} |
|
| 1119 |
+ |
|
| 1120 |
+// A MetaHeadersFrame is the representation of one HEADERS frame and |
|
| 1121 |
+// zero or more contiguous CONTINUATION frames and the decoding of |
|
| 1122 |
+// their HPACK-encoded contents. |
|
| 1123 |
+// |
|
| 1124 |
+// This type of frame does not appear on the wire and is only returned |
|
| 1125 |
+// by the Framer when Framer.ReadMetaHeaders is set. |
|
| 1126 |
+type MetaHeadersFrame struct {
|
|
| 1127 |
+ *HeadersFrame |
|
| 1128 |
+ |
|
| 1129 |
+ // Fields are the fields contained in the HEADERS and |
|
| 1130 |
+ // CONTINUATION frames. The underlying slice is owned by the |
|
| 1131 |
+ // Framer and must not be retained after the next call to |
|
| 1132 |
+ // ReadFrame. |
|
| 1133 |
+ // |
|
| 1134 |
+ // Fields are guaranteed to be in the correct http2 order and |
|
| 1135 |
+ // not have unknown pseudo header fields or invalid header |
|
| 1136 |
+ // field names or values. Required pseudo header fields may be |
|
| 1137 |
+ // missing, however. Use the MetaHeadersFrame.Pseudo accessor |
|
| 1138 |
+ // method access pseudo headers. |
|
| 1139 |
+ Fields []hpack.HeaderField |
|
| 1140 |
+ |
|
| 1141 |
+ // Truncated is whether the max header list size limit was hit |
|
| 1142 |
+ // and Fields is incomplete. The hpack decoder state is still |
|
| 1143 |
+ // valid, however. |
|
| 1144 |
+ Truncated bool |
|
| 1145 |
+} |
|
| 1146 |
+ |
|
| 1147 |
+// PseudoValue returns the given pseudo header field's value. |
|
| 1148 |
+// The provided pseudo field should not contain the leading colon. |
|
| 1149 |
+func (mh *MetaHeadersFrame) PseudoValue(pseudo string) string {
|
|
| 1150 |
+ for _, hf := range mh.Fields {
|
|
| 1151 |
+ if !hf.IsPseudo() {
|
|
| 1152 |
+ return "" |
|
| 1153 |
+ } |
|
| 1154 |
+ if hf.Name[1:] == pseudo {
|
|
| 1155 |
+ return hf.Value |
|
| 1156 |
+ } |
|
| 1157 |
+ } |
|
| 1158 |
+ return "" |
|
| 1159 |
+} |
|
| 1160 |
+ |
|
| 1161 |
+// RegularFields returns the regular (non-pseudo) header fields of mh. |
|
| 1162 |
+// The caller does not own the returned slice. |
|
| 1163 |
+func (mh *MetaHeadersFrame) RegularFields() []hpack.HeaderField {
|
|
| 1164 |
+ for i, hf := range mh.Fields {
|
|
| 1165 |
+ if !hf.IsPseudo() {
|
|
| 1166 |
+ return mh.Fields[i:] |
|
| 1167 |
+ } |
|
| 1168 |
+ } |
|
| 1169 |
+ return nil |
|
| 1170 |
+} |
|
| 1171 |
+ |
|
| 1172 |
+// PseudoFields returns the pseudo header fields of mh. |
|
| 1173 |
+// The caller does not own the returned slice. |
|
| 1174 |
+func (mh *MetaHeadersFrame) PseudoFields() []hpack.HeaderField {
|
|
| 1175 |
+ for i, hf := range mh.Fields {
|
|
| 1176 |
+ if !hf.IsPseudo() {
|
|
| 1177 |
+ return mh.Fields[:i] |
|
| 1178 |
+ } |
|
| 1179 |
+ } |
|
| 1180 |
+ return mh.Fields |
|
| 1181 |
+} |
|
| 1182 |
+ |
|
| 1183 |
+func (mh *MetaHeadersFrame) checkPseudos() error {
|
|
| 1184 |
+ var isRequest, isResponse bool |
|
| 1185 |
+ pf := mh.PseudoFields() |
|
| 1186 |
+ for i, hf := range pf {
|
|
| 1187 |
+ switch hf.Name {
|
|
| 1188 |
+ case ":method", ":path", ":scheme", ":authority": |
|
| 1189 |
+ isRequest = true |
|
| 1190 |
+ case ":status": |
|
| 1191 |
+ isResponse = true |
|
| 1192 |
+ default: |
|
| 1193 |
+ return pseudoHeaderError(hf.Name) |
|
| 1194 |
+ } |
|
| 1195 |
+ // Check for duplicates. |
|
| 1196 |
+ // This would be a bad algorithm, but N is 4. |
|
| 1197 |
+ // And this doesn't allocate. |
|
| 1198 |
+ for _, hf2 := range pf[:i] {
|
|
| 1199 |
+ if hf.Name == hf2.Name {
|
|
| 1200 |
+ return duplicatePseudoHeaderError(hf.Name) |
|
| 1201 |
+ } |
|
| 1202 |
+ } |
|
| 1203 |
+ } |
|
| 1204 |
+ if isRequest && isResponse {
|
|
| 1205 |
+ return errMixPseudoHeaderTypes |
|
| 1206 |
+ } |
|
| 1207 |
+ return nil |
|
| 1208 |
+} |
|
| 1209 |
+ |
|
| 1210 |
+func (fr *Framer) maxHeaderStringLen() int {
|
|
| 1211 |
+ v := fr.maxHeaderListSize() |
|
| 1212 |
+ if uint32(int(v)) == v {
|
|
| 1213 |
+ return int(v) |
|
| 1214 |
+ } |
|
| 1215 |
+ // They had a crazy big number for MaxHeaderBytes anyway, |
|
| 1216 |
+ // so give them unlimited header lengths: |
|
| 1217 |
+ return 0 |
|
| 1218 |
+} |
|
| 1219 |
+ |
|
| 1220 |
+// readMetaFrame returns 0 or more CONTINUATION frames from fr and |
|
| 1221 |
+// merge them into into the provided hf and returns a MetaHeadersFrame |
|
| 1222 |
+// with the decoded hpack values. |
|
| 1223 |
+func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
|
| 1224 |
+ if fr.AllowIllegalReads {
|
|
| 1225 |
+ return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders")
|
|
| 1226 |
+ } |
|
| 1227 |
+ mh := &MetaHeadersFrame{
|
|
| 1228 |
+ HeadersFrame: hf, |
|
| 1229 |
+ } |
|
| 1230 |
+ var remainSize = fr.maxHeaderListSize() |
|
| 1231 |
+ var sawRegular bool |
|
| 1232 |
+ |
|
| 1233 |
+ var invalid error // pseudo header field errors |
|
| 1234 |
+ hdec := fr.ReadMetaHeaders |
|
| 1235 |
+ hdec.SetEmitEnabled(true) |
|
| 1236 |
+ hdec.SetMaxStringLength(fr.maxHeaderStringLen()) |
|
| 1237 |
+ hdec.SetEmitFunc(func(hf hpack.HeaderField) {
|
|
| 1238 |
+ if !validHeaderFieldValue(hf.Value) {
|
|
| 1239 |
+ invalid = headerFieldValueError(hf.Value) |
|
| 1240 |
+ } |
|
| 1241 |
+ isPseudo := strings.HasPrefix(hf.Name, ":") |
|
| 1242 |
+ if isPseudo {
|
|
| 1243 |
+ if sawRegular {
|
|
| 1244 |
+ invalid = errPseudoAfterRegular |
|
| 1245 |
+ } |
|
| 1246 |
+ } else {
|
|
| 1247 |
+ sawRegular = true |
|
| 1248 |
+ if !validHeaderFieldName(hf.Name) {
|
|
| 1249 |
+ invalid = headerFieldNameError(hf.Name) |
|
| 1250 |
+ } |
|
| 1251 |
+ } |
|
| 1252 |
+ |
|
| 1253 |
+ if invalid != nil {
|
|
| 1254 |
+ hdec.SetEmitEnabled(false) |
|
| 1255 |
+ return |
|
| 1256 |
+ } |
|
| 1257 |
+ |
|
| 1258 |
+ size := hf.Size() |
|
| 1259 |
+ if size > remainSize {
|
|
| 1260 |
+ hdec.SetEmitEnabled(false) |
|
| 1261 |
+ mh.Truncated = true |
|
| 1262 |
+ return |
|
| 1263 |
+ } |
|
| 1264 |
+ remainSize -= size |
|
| 1265 |
+ |
|
| 1266 |
+ mh.Fields = append(mh.Fields, hf) |
|
| 1267 |
+ }) |
|
| 1268 |
+ // Lose reference to MetaHeadersFrame: |
|
| 1269 |
+ defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {})
|
|
| 1270 |
+ |
|
| 1271 |
+ var hc headersOrContinuation = hf |
|
| 1272 |
+ for {
|
|
| 1273 |
+ frag := hc.HeaderBlockFragment() |
|
| 1274 |
+ if _, err := hdec.Write(frag); err != nil {
|
|
| 1275 |
+ return nil, ConnectionError(ErrCodeCompression) |
|
| 1276 |
+ } |
|
| 1277 |
+ |
|
| 1278 |
+ if hc.HeadersEnded() {
|
|
| 1279 |
+ break |
|
| 1280 |
+ } |
|
| 1281 |
+ if f, err := fr.ReadFrame(); err != nil {
|
|
| 1282 |
+ return nil, err |
|
| 1283 |
+ } else {
|
|
| 1284 |
+ hc = f.(*ContinuationFrame) // guaranteed by checkFrameOrder |
|
| 1285 |
+ } |
|
| 1286 |
+ } |
|
| 1287 |
+ |
|
| 1288 |
+ mh.HeadersFrame.headerFragBuf = nil |
|
| 1289 |
+ mh.HeadersFrame.invalidate() |
|
| 1290 |
+ |
|
| 1291 |
+ if err := hdec.Close(); err != nil {
|
|
| 1292 |
+ return nil, ConnectionError(ErrCodeCompression) |
|
| 1293 |
+ } |
|
| 1294 |
+ if invalid != nil {
|
|
| 1295 |
+ fr.errDetail = invalid |
|
| 1296 |
+ return nil, StreamError{mh.StreamID, ErrCodeProtocol}
|
|
| 1297 |
+ } |
|
| 1298 |
+ if err := mh.checkPseudos(); err != nil {
|
|
| 1299 |
+ fr.errDetail = err |
|
| 1300 |
+ return nil, StreamError{mh.StreamID, ErrCodeProtocol}
|
|
| 1301 |
+ } |
|
| 1302 |
+ return mh, nil |
|
| 1303 |
+} |
|
| 1304 |
+ |
|
| 1305 |
+func summarizeFrame(f Frame) string {
|
|
| 1306 |
+ var buf bytes.Buffer |
|
| 1307 |
+ f.Header().writeDebug(&buf) |
|
| 1308 |
+ switch f := f.(type) {
|
|
| 1309 |
+ case *SettingsFrame: |
|
| 1310 |
+ n := 0 |
|
| 1311 |
+ f.ForeachSetting(func(s Setting) error {
|
|
| 1312 |
+ n++ |
|
| 1313 |
+ if n == 1 {
|
|
| 1314 |
+ buf.WriteString(", settings:")
|
|
| 1315 |
+ } |
|
| 1316 |
+ fmt.Fprintf(&buf, " %v=%v,", s.ID, s.Val) |
|
| 1317 |
+ return nil |
|
| 1318 |
+ }) |
|
| 1319 |
+ if n > 0 {
|
|
| 1320 |
+ buf.Truncate(buf.Len() - 1) // remove trailing comma |
|
| 1321 |
+ } |
|
| 1322 |
+ case *DataFrame: |
|
| 1323 |
+ data := f.Data() |
|
| 1324 |
+ const max = 256 |
|
| 1325 |
+ if len(data) > max {
|
|
| 1326 |
+ data = data[:max] |
|
| 1327 |
+ } |
|
| 1328 |
+ fmt.Fprintf(&buf, " data=%q", data) |
|
| 1329 |
+ if len(f.Data()) > max {
|
|
| 1330 |
+ fmt.Fprintf(&buf, " (%d bytes omitted)", len(f.Data())-max) |
|
| 1331 |
+ } |
|
| 1332 |
+ case *WindowUpdateFrame: |
|
| 1333 |
+ if f.StreamID == 0 {
|
|
| 1334 |
+ buf.WriteString(" (conn)")
|
|
| 1335 |
+ } |
|
| 1336 |
+ fmt.Fprintf(&buf, " incr=%v", f.Increment) |
|
| 1337 |
+ case *PingFrame: |
|
| 1338 |
+ fmt.Fprintf(&buf, " ping=%q", f.Data[:]) |
|
| 1339 |
+ case *GoAwayFrame: |
|
| 1340 |
+ fmt.Fprintf(&buf, " LastStreamID=%v ErrCode=%v Debug=%q", |
|
| 1341 |
+ f.LastStreamID, f.ErrCode, f.debugData) |
|
| 1342 |
+ case *RSTStreamFrame: |
|
| 1343 |
+ fmt.Fprintf(&buf, " ErrCode=%v", f.ErrCode) |
|
| 1344 |
+ } |
|
| 1345 |
+ return buf.String() |
|
| 1346 |
+} |
| 1114 | 1347 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build go1.5 |
|
| 5 |
+ |
|
| 6 |
+package http2 |
|
| 7 |
+ |
|
| 8 |
+import "net/http" |
|
| 9 |
+ |
|
| 10 |
+func requestCancel(req *http.Request) <-chan struct{} { return req.Cancel }
|
| ... | ... |
@@ -1,9 +1,6 @@ |
| 1 | 1 |
// Copyright 2014 The Go Authors. All rights reserved. |
| 2 | 2 |
// Use of this source code is governed by a BSD-style |
| 3 | 3 |
// license that can be found in the LICENSE file. |
| 4 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 5 |
-// Licensed under the same terms as Go itself: |
|
| 6 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 7 | 4 |
|
| 8 | 5 |
// Defensive debug-only utility to track that functions run on the |
| 9 | 6 |
// goroutine that they're supposed to. |
| ... | ... |
@@ -1,9 +1,6 @@ |
| 1 | 1 |
// Copyright 2014 The Go Authors. All rights reserved. |
| 2 | 2 |
// Use of this source code is governed by a BSD-style |
| 3 | 3 |
// license that can be found in the LICENSE file. |
| 4 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 5 |
-// Licensed under the same terms as Go itself: |
|
| 6 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 7 | 4 |
|
| 8 | 5 |
package http2 |
| 9 | 6 |
|
| ... | ... |
@@ -60,6 +57,7 @@ func init() {
|
| 60 | 60 |
"server", |
| 61 | 61 |
"set-cookie", |
| 62 | 62 |
"strict-transport-security", |
| 63 |
+ "trailer", |
|
| 63 | 64 |
"transfer-encoding", |
| 64 | 65 |
"user-agent", |
| 65 | 66 |
"vary", |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
package hpack |
| 7 | 6 |
|
| ... | ... |
@@ -145,7 +144,7 @@ func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
|
| 145 | 145 |
|
| 146 | 146 |
// shouldIndex reports whether f should be indexed. |
| 147 | 147 |
func (e *Encoder) shouldIndex(f HeaderField) bool {
|
| 148 |
- return !f.Sensitive && f.size() <= e.dynTab.maxSize |
|
| 148 |
+ return !f.Sensitive && f.Size() <= e.dynTab.maxSize |
|
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 | 151 |
// appendIndexed appends index i, as encoded in "Indexed Header Field" |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
// Package hpack implements HPACK, a compression format for |
| 7 | 6 |
// efficiently representing HTTP header fields in the context of HTTP/2. |
| ... | ... |
@@ -42,7 +41,24 @@ type HeaderField struct {
|
| 42 | 42 |
Sensitive bool |
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
-func (hf *HeaderField) size() uint32 {
|
|
| 45 |
+// IsPseudo reports whether the header field is an http2 pseudo header. |
|
| 46 |
+// That is, it reports whether it starts with a colon. |
|
| 47 |
+// It is not otherwise guaranteed to be a valid psuedo header field, |
|
| 48 |
+// though. |
|
| 49 |
+func (hf HeaderField) IsPseudo() bool {
|
|
| 50 |
+ return len(hf.Name) != 0 && hf.Name[0] == ':' |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func (hf HeaderField) String() string {
|
|
| 54 |
+ var suffix string |
|
| 55 |
+ if hf.Sensitive {
|
|
| 56 |
+ suffix = " (sensitive)" |
|
| 57 |
+ } |
|
| 58 |
+ return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
|
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// Size returns the size of an entry per RFC 7540 section 5.2. |
|
| 62 |
+func (hf HeaderField) Size() uint32 {
|
|
| 46 | 63 |
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1 |
| 47 | 64 |
// "The size of the dynamic table is the sum of the size of |
| 48 | 65 |
// its entries. The size of an entry is the sum of its name's |
| ... | ... |
@@ -64,23 +80,65 @@ type Decoder struct {
|
| 64 | 64 |
dynTab dynamicTable |
| 65 | 65 |
emit func(f HeaderField) |
| 66 | 66 |
|
| 67 |
+ emitEnabled bool // whether calls to emit are enabled |
|
| 68 |
+ maxStrLen int // 0 means unlimited |
|
| 69 |
+ |
|
| 67 | 70 |
// buf is the unparsed buffer. It's only written to |
| 68 | 71 |
// saveBuf if it was truncated in the middle of a header |
| 69 | 72 |
// block. Because it's usually not owned, we can only |
| 70 | 73 |
// process it under Write. |
| 71 |
- buf []byte // usually not owned |
|
| 74 |
+ buf []byte // not owned; only valid during Write |
|
| 75 |
+ |
|
| 76 |
+ // saveBuf is previous data passed to Write which we weren't able |
|
| 77 |
+ // to fully parse before. Unlike buf, we own this data. |
|
| 72 | 78 |
saveBuf bytes.Buffer |
| 73 | 79 |
} |
| 74 | 80 |
|
| 75 |
-func NewDecoder(maxSize uint32, emitFunc func(f HeaderField)) *Decoder {
|
|
| 81 |
+// NewDecoder returns a new decoder with the provided maximum dynamic |
|
| 82 |
+// table size. The emitFunc will be called for each valid field |
|
| 83 |
+// parsed, in the same goroutine as calls to Write, before Write returns. |
|
| 84 |
+func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decoder {
|
|
| 76 | 85 |
d := &Decoder{
|
| 77 |
- emit: emitFunc, |
|
| 86 |
+ emit: emitFunc, |
|
| 87 |
+ emitEnabled: true, |
|
| 78 | 88 |
} |
| 79 |
- d.dynTab.allowedMaxSize = maxSize |
|
| 80 |
- d.dynTab.setMaxSize(maxSize) |
|
| 89 |
+ d.dynTab.allowedMaxSize = maxDynamicTableSize |
|
| 90 |
+ d.dynTab.setMaxSize(maxDynamicTableSize) |
|
| 81 | 91 |
return d |
| 82 | 92 |
} |
| 83 | 93 |
|
| 94 |
+// ErrStringLength is returned by Decoder.Write when the max string length |
|
| 95 |
+// (as configured by Decoder.SetMaxStringLength) would be violated. |
|
| 96 |
+var ErrStringLength = errors.New("hpack: string too long")
|
|
| 97 |
+ |
|
| 98 |
+// SetMaxStringLength sets the maximum size of a HeaderField name or |
|
| 99 |
+// value string. If a string exceeds this length (even after any |
|
| 100 |
+// decompression), Write will return ErrStringLength. |
|
| 101 |
+// A value of 0 means unlimited and is the default from NewDecoder. |
|
| 102 |
+func (d *Decoder) SetMaxStringLength(n int) {
|
|
| 103 |
+ d.maxStrLen = n |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// SetEmitFunc changes the callback used when new header fields |
|
| 107 |
+// are decoded. |
|
| 108 |
+// It must be non-nil. It does not affect EmitEnabled. |
|
| 109 |
+func (d *Decoder) SetEmitFunc(emitFunc func(f HeaderField)) {
|
|
| 110 |
+ d.emit = emitFunc |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+// SetEmitEnabled controls whether the emitFunc provided to NewDecoder |
|
| 114 |
+// should be called. The default is true. |
|
| 115 |
+// |
|
| 116 |
+// This facility exists to let servers enforce MAX_HEADER_LIST_SIZE |
|
| 117 |
+// while still decoding and keeping in-sync with decoder state, but |
|
| 118 |
+// without doing unnecessary decompression or generating unnecessary |
|
| 119 |
+// garbage for header fields past the limit. |
|
| 120 |
+func (d *Decoder) SetEmitEnabled(v bool) { d.emitEnabled = v }
|
|
| 121 |
+ |
|
| 122 |
+// EmitEnabled reports whether calls to the emitFunc provided to NewDecoder |
|
| 123 |
+// are currently enabled. The default is true. |
|
| 124 |
+func (d *Decoder) EmitEnabled() bool { return d.emitEnabled }
|
|
| 125 |
+ |
|
| 84 | 126 |
// TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their |
| 85 | 127 |
// underlying buffers for garbage reasons. |
| 86 | 128 |
|
| ... | ... |
@@ -122,7 +180,7 @@ func (dt *dynamicTable) setMaxSize(v uint32) {
|
| 122 | 122 |
|
| 123 | 123 |
func (dt *dynamicTable) add(f HeaderField) {
|
| 124 | 124 |
dt.ents = append(dt.ents, f) |
| 125 |
- dt.size += f.size() |
|
| 125 |
+ dt.size += f.Size() |
|
| 126 | 126 |
dt.evict() |
| 127 | 127 |
} |
| 128 | 128 |
|
| ... | ... |
@@ -130,7 +188,7 @@ func (dt *dynamicTable) add(f HeaderField) {
|
| 130 | 130 |
func (dt *dynamicTable) evict() {
|
| 131 | 131 |
base := dt.ents // keep base pointer of slice |
| 132 | 132 |
for dt.size > dt.maxSize {
|
| 133 |
- dt.size -= dt.ents[0].size() |
|
| 133 |
+ dt.size -= dt.ents[0].Size() |
|
| 134 | 134 |
dt.ents = dt.ents[1:] |
| 135 | 135 |
} |
| 136 | 136 |
|
| ... | ... |
@@ -247,15 +305,23 @@ func (d *Decoder) Write(p []byte) (n int, err error) {
|
| 247 | 247 |
|
| 248 | 248 |
for len(d.buf) > 0 {
|
| 249 | 249 |
err = d.parseHeaderFieldRepr() |
| 250 |
- if err != nil {
|
|
| 251 |
- if err == errNeedMore {
|
|
| 252 |
- err = nil |
|
| 253 |
- d.saveBuf.Write(d.buf) |
|
| 250 |
+ if err == errNeedMore {
|
|
| 251 |
+ // Extra paranoia, making sure saveBuf won't |
|
| 252 |
+ // get too large. All the varint and string |
|
| 253 |
+ // reading code earlier should already catch |
|
| 254 |
+ // overlong things and return ErrStringLength, |
|
| 255 |
+ // but keep this as a last resort. |
|
| 256 |
+ const varIntOverhead = 8 // conservative |
|
| 257 |
+ if d.maxStrLen != 0 && int64(len(d.buf)) > 2*(int64(d.maxStrLen)+varIntOverhead) {
|
|
| 258 |
+ return 0, ErrStringLength |
|
| 254 | 259 |
} |
| 260 |
+ d.saveBuf.Write(d.buf) |
|
| 261 |
+ return len(p), nil |
|
| 262 |
+ } |
|
| 263 |
+ if err != nil {
|
|
| 255 | 264 |
break |
| 256 | 265 |
} |
| 257 | 266 |
} |
| 258 |
- |
|
| 259 | 267 |
return len(p), err |
| 260 | 268 |
} |
| 261 | 269 |
|
| ... | ... |
@@ -323,9 +389,8 @@ func (d *Decoder) parseFieldIndexed() error {
|
| 323 | 323 |
if !ok {
|
| 324 | 324 |
return DecodingError{InvalidIndexError(idx)}
|
| 325 | 325 |
} |
| 326 |
- d.emit(HeaderField{Name: hf.Name, Value: hf.Value})
|
|
| 327 | 326 |
d.buf = buf |
| 328 |
- return nil |
|
| 327 |
+ return d.callEmit(HeaderField{Name: hf.Name, Value: hf.Value})
|
|
| 329 | 328 |
} |
| 330 | 329 |
|
| 331 | 330 |
// (same invariants and behavior as parseHeaderFieldRepr) |
| ... | ... |
@@ -337,6 +402,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
| 337 | 337 |
} |
| 338 | 338 |
|
| 339 | 339 |
var hf HeaderField |
| 340 |
+ wantStr := d.emitEnabled || it.indexed() |
|
| 340 | 341 |
if nameIdx > 0 {
|
| 341 | 342 |
ihf, ok := d.at(nameIdx) |
| 342 | 343 |
if !ok {
|
| ... | ... |
@@ -344,12 +410,12 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
| 344 | 344 |
} |
| 345 | 345 |
hf.Name = ihf.Name |
| 346 | 346 |
} else {
|
| 347 |
- hf.Name, buf, err = readString(buf) |
|
| 347 |
+ hf.Name, buf, err = d.readString(buf, wantStr) |
|
| 348 | 348 |
if err != nil {
|
| 349 | 349 |
return err |
| 350 | 350 |
} |
| 351 | 351 |
} |
| 352 |
- hf.Value, buf, err = readString(buf) |
|
| 352 |
+ hf.Value, buf, err = d.readString(buf, wantStr) |
|
| 353 | 353 |
if err != nil {
|
| 354 | 354 |
return err |
| 355 | 355 |
} |
| ... | ... |
@@ -358,7 +424,18 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
| 358 | 358 |
d.dynTab.add(hf) |
| 359 | 359 |
} |
| 360 | 360 |
hf.Sensitive = it.sensitive() |
| 361 |
- d.emit(hf) |
|
| 361 |
+ return d.callEmit(hf) |
|
| 362 |
+} |
|
| 363 |
+ |
|
| 364 |
+func (d *Decoder) callEmit(hf HeaderField) error {
|
|
| 365 |
+ if d.maxStrLen != 0 {
|
|
| 366 |
+ if len(hf.Name) > d.maxStrLen || len(hf.Value) > d.maxStrLen {
|
|
| 367 |
+ return ErrStringLength |
|
| 368 |
+ } |
|
| 369 |
+ } |
|
| 370 |
+ if d.emitEnabled {
|
|
| 371 |
+ d.emit(hf) |
|
| 372 |
+ } |
|
| 362 | 373 |
return nil |
| 363 | 374 |
} |
| 364 | 375 |
|
| ... | ... |
@@ -420,7 +497,15 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
|
| 420 | 420 |
return 0, origP, errNeedMore |
| 421 | 421 |
} |
| 422 | 422 |
|
| 423 |
-func readString(p []byte) (s string, remain []byte, err error) {
|
|
| 423 |
+// readString decodes an hpack string from p. |
|
| 424 |
+// |
|
| 425 |
+// wantStr is whether s will be used. If false, decompression and |
|
| 426 |
+// []byte->string garbage are skipped if s will be ignored |
|
| 427 |
+// anyway. This does mean that huffman decoding errors for non-indexed |
|
| 428 |
+// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server |
|
| 429 |
+// is returning an error anyway, and because they're not indexed, the error |
|
| 430 |
+// won't affect the decoding state. |
|
| 431 |
+func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) {
|
|
| 424 | 432 |
if len(p) == 0 {
|
| 425 | 433 |
return "", p, errNeedMore |
| 426 | 434 |
} |
| ... | ... |
@@ -429,17 +514,29 @@ func readString(p []byte) (s string, remain []byte, err error) {
|
| 429 | 429 |
if err != nil {
|
| 430 | 430 |
return "", p, err |
| 431 | 431 |
} |
| 432 |
+ if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) {
|
|
| 433 |
+ return "", nil, ErrStringLength |
|
| 434 |
+ } |
|
| 432 | 435 |
if uint64(len(p)) < strLen {
|
| 433 | 436 |
return "", p, errNeedMore |
| 434 | 437 |
} |
| 435 | 438 |
if !isHuff {
|
| 436 |
- return string(p[:strLen]), p[strLen:], nil |
|
| 439 |
+ if wantStr {
|
|
| 440 |
+ s = string(p[:strLen]) |
|
| 441 |
+ } |
|
| 442 |
+ return s, p[strLen:], nil |
|
| 437 | 443 |
} |
| 438 | 444 |
|
| 439 |
- // TODO: optimize this garbage: |
|
| 440 |
- var buf bytes.Buffer |
|
| 441 |
- if _, err := HuffmanDecode(&buf, p[:strLen]); err != nil {
|
|
| 442 |
- return "", nil, err |
|
| 445 |
+ if wantStr {
|
|
| 446 |
+ buf := bufPool.Get().(*bytes.Buffer) |
|
| 447 |
+ buf.Reset() // don't trust others |
|
| 448 |
+ defer bufPool.Put(buf) |
|
| 449 |
+ if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil {
|
|
| 450 |
+ buf.Reset() |
|
| 451 |
+ return "", nil, err |
|
| 452 |
+ } |
|
| 453 |
+ s = buf.String() |
|
| 454 |
+ buf.Reset() // be nice to GC |
|
| 443 | 455 |
} |
| 444 |
- return buf.String(), p[strLen:], nil |
|
| 456 |
+ return s, p[strLen:], nil |
|
| 445 | 457 |
} |
| ... | ... |
@@ -1,12 +1,12 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
package hpack |
| 7 | 6 |
|
| 8 | 7 |
import ( |
| 9 | 8 |
"bytes" |
| 9 |
+ "errors" |
|
| 10 | 10 |
"io" |
| 11 | 11 |
"sync" |
| 12 | 12 |
) |
| ... | ... |
@@ -22,15 +22,46 @@ func HuffmanDecode(w io.Writer, v []byte) (int, error) {
|
| 22 | 22 |
buf := bufPool.Get().(*bytes.Buffer) |
| 23 | 23 |
buf.Reset() |
| 24 | 24 |
defer bufPool.Put(buf) |
| 25 |
+ if err := huffmanDecode(buf, 0, v); err != nil {
|
|
| 26 |
+ return 0, err |
|
| 27 |
+ } |
|
| 28 |
+ return w.Write(buf.Bytes()) |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// HuffmanDecodeToString decodes the string in v. |
|
| 32 |
+func HuffmanDecodeToString(v []byte) (string, error) {
|
|
| 33 |
+ buf := bufPool.Get().(*bytes.Buffer) |
|
| 34 |
+ buf.Reset() |
|
| 35 |
+ defer bufPool.Put(buf) |
|
| 36 |
+ if err := huffmanDecode(buf, 0, v); err != nil {
|
|
| 37 |
+ return "", err |
|
| 38 |
+ } |
|
| 39 |
+ return buf.String(), nil |
|
| 40 |
+} |
|
| 25 | 41 |
|
| 42 |
+// ErrInvalidHuffman is returned for errors found decoding |
|
| 43 |
+// Huffman-encoded strings. |
|
| 44 |
+var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
|
|
| 45 |
+ |
|
| 46 |
+// huffmanDecode decodes v to buf. |
|
| 47 |
+// If maxLen is greater than 0, attempts to write more to buf than |
|
| 48 |
+// maxLen bytes will return ErrStringLength. |
|
| 49 |
+func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
|
| 26 | 50 |
n := rootHuffmanNode |
| 27 | 51 |
cur, nbits := uint(0), uint8(0) |
| 28 | 52 |
for _, b := range v {
|
| 29 | 53 |
cur = cur<<8 | uint(b) |
| 30 | 54 |
nbits += 8 |
| 31 | 55 |
for nbits >= 8 {
|
| 32 |
- n = n.children[byte(cur>>(nbits-8))] |
|
| 56 |
+ idx := byte(cur >> (nbits - 8)) |
|
| 57 |
+ n = n.children[idx] |
|
| 58 |
+ if n == nil {
|
|
| 59 |
+ return ErrInvalidHuffman |
|
| 60 |
+ } |
|
| 33 | 61 |
if n.children == nil {
|
| 62 |
+ if maxLen != 0 && buf.Len() == maxLen {
|
|
| 63 |
+ return ErrStringLength |
|
| 64 |
+ } |
|
| 34 | 65 |
buf.WriteByte(n.sym) |
| 35 | 66 |
nbits -= n.codeLen |
| 36 | 67 |
n = rootHuffmanNode |
| ... | ... |
@@ -48,7 +79,7 @@ func HuffmanDecode(w io.Writer, v []byte) (int, error) {
|
| 48 | 48 |
nbits -= n.codeLen |
| 49 | 49 |
n = rootHuffmanNode |
| 50 | 50 |
} |
| 51 |
- return w.Write(buf.Bytes()) |
|
| 51 |
+ return nil |
|
| 52 | 52 |
} |
| 53 | 53 |
|
| 54 | 54 |
type node struct {
|
| ... | ... |
@@ -67,10 +98,10 @@ func newInternalNode() *node {
|
| 67 | 67 |
var rootHuffmanNode = newInternalNode() |
| 68 | 68 |
|
| 69 | 69 |
func init() {
|
| 70 |
+ if len(huffmanCodes) != 256 {
|
|
| 71 |
+ panic("unexpected size")
|
|
| 72 |
+ } |
|
| 70 | 73 |
for i, code := range huffmanCodes {
|
| 71 |
- if i > 255 {
|
|
| 72 |
- panic("too many huffman codes")
|
|
| 73 |
- } |
|
| 74 | 74 |
addDecoderNode(byte(i), code, huffmanCodeLen[i]) |
| 75 | 75 |
} |
| 76 | 76 |
} |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
package hpack |
| 7 | 6 |
|
| ... | ... |
@@ -10,7 +9,7 @@ func pair(name, value string) HeaderField {
|
| 10 | 10 |
} |
| 11 | 11 |
|
| 12 | 12 |
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B |
| 13 |
-var staticTable = []HeaderField{
|
|
| 13 |
+var staticTable = [...]HeaderField{
|
|
| 14 | 14 |
pair(":authority", ""), // index 1 (1-based)
|
| 15 | 15 |
pair(":method", "GET"),
|
| 16 | 16 |
pair(":method", "POST"),
|
| ... | ... |
@@ -74,7 +73,7 @@ var staticTable = []HeaderField{
|
| 74 | 74 |
pair("www-authenticate", ""),
|
| 75 | 75 |
} |
| 76 | 76 |
|
| 77 |
-var huffmanCodes = []uint32{
|
|
| 77 |
+var huffmanCodes = [256]uint32{
|
|
| 78 | 78 |
0x1ff8, |
| 79 | 79 |
0x7fffd8, |
| 80 | 80 |
0xfffffe2, |
| ... | ... |
@@ -333,7 +332,7 @@ var huffmanCodes = []uint32{
|
| 333 | 333 |
0x3ffffee, |
| 334 | 334 |
} |
| 335 | 335 |
|
| 336 |
-var huffmanCodeLen = []uint8{
|
|
| 336 |
+var huffmanCodeLen = [256]uint8{
|
|
| 337 | 337 |
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, |
| 338 | 338 |
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 339 | 339 |
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, |
| ... | ... |
@@ -1,31 +1,51 @@ |
| 1 | 1 |
// Copyright 2014 The Go Authors. All rights reserved. |
| 2 | 2 |
// Use of this source code is governed by a BSD-style |
| 3 | 3 |
// license that can be found in the LICENSE file. |
| 4 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 5 |
-// Licensed under the same terms as Go itself: |
|
| 6 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 7 | 4 |
|
| 8 | 5 |
// Package http2 implements the HTTP/2 protocol. |
| 9 | 6 |
// |
| 10 |
-// This is a work in progress. This package is low-level and intended |
|
| 11 |
-// to be used directly by very few people. Most users will use it |
|
| 12 |
-// indirectly through integration with the net/http package. See |
|
| 13 |
-// ConfigureServer. That ConfigureServer call will likely be automatic |
|
| 14 |
-// or available via an empty import in the future. |
|
| 7 |
+// This package is low-level and intended to be used directly by very |
|
| 8 |
+// few people. Most users will use it indirectly through the automatic |
|
| 9 |
+// use by the net/http package (from Go 1.6 and later). |
|
| 10 |
+// For use in earlier Go versions see ConfigureServer. (Transport support |
|
| 11 |
+// requires Go 1.6 or later) |
|
| 15 | 12 |
// |
| 16 |
-// See http://http2.github.io/ |
|
| 13 |
+// See https://http2.github.io/ for more information on HTTP/2. |
|
| 14 |
+// |
|
| 15 |
+// See https://http2.golang.org/ for a test server running this code. |
|
| 17 | 16 |
package http2 |
| 18 | 17 |
|
| 19 | 18 |
import ( |
| 20 | 19 |
"bufio" |
| 20 |
+ "crypto/tls" |
|
| 21 |
+ "errors" |
|
| 21 | 22 |
"fmt" |
| 22 | 23 |
"io" |
| 23 | 24 |
"net/http" |
| 25 |
+ "os" |
|
| 26 |
+ "sort" |
|
| 24 | 27 |
"strconv" |
| 28 |
+ "strings" |
|
| 25 | 29 |
"sync" |
| 26 | 30 |
) |
| 27 | 31 |
|
| 28 |
-var VerboseLogs = false |
|
| 32 |
+var ( |
|
| 33 |
+ VerboseLogs bool |
|
| 34 |
+ logFrameWrites bool |
|
| 35 |
+ logFrameReads bool |
|
| 36 |
+) |
|
| 37 |
+ |
|
| 38 |
+func init() {
|
|
| 39 |
+ e := os.Getenv("GODEBUG")
|
|
| 40 |
+ if strings.Contains(e, "http2debug=1") {
|
|
| 41 |
+ VerboseLogs = true |
|
| 42 |
+ } |
|
| 43 |
+ if strings.Contains(e, "http2debug=2") {
|
|
| 44 |
+ VerboseLogs = true |
|
| 45 |
+ logFrameWrites = true |
|
| 46 |
+ logFrameReads = true |
|
| 47 |
+ } |
|
| 48 |
+} |
|
| 29 | 49 |
|
| 30 | 50 |
const ( |
| 31 | 51 |
// ClientPreface is the string that must be sent by new |
| ... | ... |
@@ -141,17 +161,63 @@ func (s SettingID) String() string {
|
| 141 | 141 |
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
| 142 | 142 |
} |
| 143 | 143 |
|
| 144 |
-func validHeader(v string) bool {
|
|
| 144 |
+var ( |
|
| 145 |
+ errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
|
| 146 |
+ errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
|
| 147 |
+) |
|
| 148 |
+ |
|
| 149 |
+// validHeaderFieldName reports whether v is a valid header field name (key). |
|
| 150 |
+// RFC 7230 says: |
|
| 151 |
+// header-field = field-name ":" OWS field-value OWS |
|
| 152 |
+// field-name = token |
|
| 153 |
+// token = 1*tchar |
|
| 154 |
+// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / |
|
| 155 |
+// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA |
|
| 156 |
+// Further, http2 says: |
|
| 157 |
+// "Just as in HTTP/1.x, header field names are strings of ASCII |
|
| 158 |
+// characters that are compared in a case-insensitive |
|
| 159 |
+// fashion. However, header field names MUST be converted to |
|
| 160 |
+// lowercase prior to their encoding in HTTP/2. " |
|
| 161 |
+func validHeaderFieldName(v string) bool {
|
|
| 145 | 162 |
if len(v) == 0 {
|
| 146 | 163 |
return false |
| 147 | 164 |
} |
| 148 | 165 |
for _, r := range v {
|
| 149 |
- // "Just as in HTTP/1.x, header field names are |
|
| 150 |
- // strings of ASCII characters that are compared in a |
|
| 151 |
- // case-insensitive fashion. However, header field |
|
| 152 |
- // names MUST be converted to lowercase prior to their |
|
| 153 |
- // encoding in HTTP/2. " |
|
| 154 |
- if r >= 127 || ('A' <= r && r <= 'Z') {
|
|
| 166 |
+ if int(r) >= len(isTokenTable) || ('A' <= r && r <= 'Z') {
|
|
| 167 |
+ return false |
|
| 168 |
+ } |
|
| 169 |
+ if !isTokenTable[byte(r)] {
|
|
| 170 |
+ return false |
|
| 171 |
+ } |
|
| 172 |
+ } |
|
| 173 |
+ return true |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+// validHeaderFieldValue reports whether v is a valid header field value. |
|
| 177 |
+// |
|
| 178 |
+// RFC 7230 says: |
|
| 179 |
+// field-value = *( field-content / obs-fold ) |
|
| 180 |
+// obj-fold = N/A to http2, and deprecated |
|
| 181 |
+// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] |
|
| 182 |
+// field-vchar = VCHAR / obs-text |
|
| 183 |
+// obs-text = %x80-FF |
|
| 184 |
+// VCHAR = "any visible [USASCII] character" |
|
| 185 |
+// |
|
| 186 |
+// http2 further says: "Similarly, HTTP/2 allows header field values |
|
| 187 |
+// that are not valid. While most of the values that can be encoded |
|
| 188 |
+// will not alter header field parsing, carriage return (CR, ASCII |
|
| 189 |
+// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII |
|
| 190 |
+// 0x0) might be exploited by an attacker if they are translated |
|
| 191 |
+// verbatim. Any request or response that contains a character not |
|
| 192 |
+// permitted in a header field value MUST be treated as malformed |
|
| 193 |
+// (Section 8.1.2.6). Valid characters are defined by the |
|
| 194 |
+// field-content ABNF rule in Section 3.2 of [RFC7230]." |
|
| 195 |
+// |
|
| 196 |
+// This function does not (yet?) properly handle the rejection of |
|
| 197 |
+// strings that begin or end with SP or HTAB. |
|
| 198 |
+func validHeaderFieldValue(v string) bool {
|
|
| 199 |
+ for i := 0; i < len(v); i++ {
|
|
| 200 |
+ if b := v[i]; b < ' ' && b != '\t' || b == 0x7f {
|
|
| 155 | 201 |
return false |
| 156 | 202 |
} |
| 157 | 203 |
} |
| ... | ... |
@@ -247,3 +313,152 @@ func (w *bufferedWriter) Flush() error {
|
| 247 | 247 |
w.bw = nil |
| 248 | 248 |
return err |
| 249 | 249 |
} |
| 250 |
+ |
|
| 251 |
+func mustUint31(v int32) uint32 {
|
|
| 252 |
+ if v < 0 || v > 2147483647 {
|
|
| 253 |
+ panic("out of range")
|
|
| 254 |
+ } |
|
| 255 |
+ return uint32(v) |
|
| 256 |
+} |
|
| 257 |
+ |
|
| 258 |
+// bodyAllowedForStatus reports whether a given response status code |
|
| 259 |
+// permits a body. See RFC2616, section 4.4. |
|
| 260 |
+func bodyAllowedForStatus(status int) bool {
|
|
| 261 |
+ switch {
|
|
| 262 |
+ case status >= 100 && status <= 199: |
|
| 263 |
+ return false |
|
| 264 |
+ case status == 204: |
|
| 265 |
+ return false |
|
| 266 |
+ case status == 304: |
|
| 267 |
+ return false |
|
| 268 |
+ } |
|
| 269 |
+ return true |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+type httpError struct {
|
|
| 273 |
+ msg string |
|
| 274 |
+ timeout bool |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+func (e *httpError) Error() string { return e.msg }
|
|
| 278 |
+func (e *httpError) Timeout() bool { return e.timeout }
|
|
| 279 |
+func (e *httpError) Temporary() bool { return true }
|
|
| 280 |
+ |
|
| 281 |
+var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true}
|
|
| 282 |
+ |
|
| 283 |
+var isTokenTable = [127]bool{
|
|
| 284 |
+ '!': true, |
|
| 285 |
+ '#': true, |
|
| 286 |
+ '$': true, |
|
| 287 |
+ '%': true, |
|
| 288 |
+ '&': true, |
|
| 289 |
+ '\'': true, |
|
| 290 |
+ '*': true, |
|
| 291 |
+ '+': true, |
|
| 292 |
+ '-': true, |
|
| 293 |
+ '.': true, |
|
| 294 |
+ '0': true, |
|
| 295 |
+ '1': true, |
|
| 296 |
+ '2': true, |
|
| 297 |
+ '3': true, |
|
| 298 |
+ '4': true, |
|
| 299 |
+ '5': true, |
|
| 300 |
+ '6': true, |
|
| 301 |
+ '7': true, |
|
| 302 |
+ '8': true, |
|
| 303 |
+ '9': true, |
|
| 304 |
+ 'A': true, |
|
| 305 |
+ 'B': true, |
|
| 306 |
+ 'C': true, |
|
| 307 |
+ 'D': true, |
|
| 308 |
+ 'E': true, |
|
| 309 |
+ 'F': true, |
|
| 310 |
+ 'G': true, |
|
| 311 |
+ 'H': true, |
|
| 312 |
+ 'I': true, |
|
| 313 |
+ 'J': true, |
|
| 314 |
+ 'K': true, |
|
| 315 |
+ 'L': true, |
|
| 316 |
+ 'M': true, |
|
| 317 |
+ 'N': true, |
|
| 318 |
+ 'O': true, |
|
| 319 |
+ 'P': true, |
|
| 320 |
+ 'Q': true, |
|
| 321 |
+ 'R': true, |
|
| 322 |
+ 'S': true, |
|
| 323 |
+ 'T': true, |
|
| 324 |
+ 'U': true, |
|
| 325 |
+ 'W': true, |
|
| 326 |
+ 'V': true, |
|
| 327 |
+ 'X': true, |
|
| 328 |
+ 'Y': true, |
|
| 329 |
+ 'Z': true, |
|
| 330 |
+ '^': true, |
|
| 331 |
+ '_': true, |
|
| 332 |
+ '`': true, |
|
| 333 |
+ 'a': true, |
|
| 334 |
+ 'b': true, |
|
| 335 |
+ 'c': true, |
|
| 336 |
+ 'd': true, |
|
| 337 |
+ 'e': true, |
|
| 338 |
+ 'f': true, |
|
| 339 |
+ 'g': true, |
|
| 340 |
+ 'h': true, |
|
| 341 |
+ 'i': true, |
|
| 342 |
+ 'j': true, |
|
| 343 |
+ 'k': true, |
|
| 344 |
+ 'l': true, |
|
| 345 |
+ 'm': true, |
|
| 346 |
+ 'n': true, |
|
| 347 |
+ 'o': true, |
|
| 348 |
+ 'p': true, |
|
| 349 |
+ 'q': true, |
|
| 350 |
+ 'r': true, |
|
| 351 |
+ 's': true, |
|
| 352 |
+ 't': true, |
|
| 353 |
+ 'u': true, |
|
| 354 |
+ 'v': true, |
|
| 355 |
+ 'w': true, |
|
| 356 |
+ 'x': true, |
|
| 357 |
+ 'y': true, |
|
| 358 |
+ 'z': true, |
|
| 359 |
+ '|': true, |
|
| 360 |
+ '~': true, |
|
| 361 |
+} |
|
| 362 |
+ |
|
| 363 |
+type connectionStater interface {
|
|
| 364 |
+ ConnectionState() tls.ConnectionState |
|
| 365 |
+} |
|
| 366 |
+ |
|
| 367 |
+var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }}
|
|
| 368 |
+ |
|
| 369 |
+type sorter struct {
|
|
| 370 |
+ v []string // owned by sorter |
|
| 371 |
+} |
|
| 372 |
+ |
|
| 373 |
+func (s *sorter) Len() int { return len(s.v) }
|
|
| 374 |
+func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] }
|
|
| 375 |
+func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] }
|
|
| 376 |
+ |
|
| 377 |
+// Keys returns the sorted keys of h. |
|
| 378 |
+// |
|
| 379 |
+// The returned slice is only valid until s used again or returned to |
|
| 380 |
+// its pool. |
|
| 381 |
+func (s *sorter) Keys(h http.Header) []string {
|
|
| 382 |
+ keys := s.v[:0] |
|
| 383 |
+ for k := range h {
|
|
| 384 |
+ keys = append(keys, k) |
|
| 385 |
+ } |
|
| 386 |
+ s.v = keys |
|
| 387 |
+ sort.Sort(s) |
|
| 388 |
+ return keys |
|
| 389 |
+} |
|
| 390 |
+ |
|
| 391 |
+func (s *sorter) SortStrings(ss []string) {
|
|
| 392 |
+ // Our sorter works on s.v, which sorter owners, so |
|
| 393 |
+ // stash it away while we sort the user's buffer. |
|
| 394 |
+ save := s.v |
|
| 395 |
+ s.v = ss |
|
| 396 |
+ sort.Sort(s) |
|
| 397 |
+ s.v = save |
|
| 398 |
+} |
| 250 | 399 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build !go1.5 |
|
| 5 |
+ |
|
| 6 |
+package http2 |
|
| 7 |
+ |
|
| 8 |
+import "net/http" |
|
| 9 |
+ |
|
| 10 |
+func requestCancel(req *http.Request) <-chan struct{} { return nil }
|
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,13 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build !go1.6 |
|
| 5 |
+ |
|
| 6 |
+package http2 |
|
| 7 |
+ |
|
| 8 |
+import "net/http" |
|
| 9 |
+ |
|
| 10 |
+func configureTransport(t1 *http.Transport) (*Transport, error) {
|
|
| 11 |
+ return nil, errTransportVersion |
|
| 12 |
+} |
| ... | ... |
@@ -1,43 +1,147 @@ |
| 1 |
-// Copyright 2014 The Go Authors. |
|
| 2 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 1 |
+// Copyright 2014 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 5 | 4 |
|
| 6 | 5 |
package http2 |
| 7 | 6 |
|
| 8 | 7 |
import ( |
| 8 |
+ "errors" |
|
| 9 |
+ "io" |
|
| 9 | 10 |
"sync" |
| 10 | 11 |
) |
| 11 | 12 |
|
| 13 |
+// pipe is a goroutine-safe io.Reader/io.Writer pair. It's like |
|
| 14 |
+// io.Pipe except there are no PipeReader/PipeWriter halves, and the |
|
| 15 |
+// underlying buffer is an interface. (io.Pipe is always unbuffered) |
|
| 12 | 16 |
type pipe struct {
|
| 13 |
- b buffer |
|
| 14 |
- c sync.Cond |
|
| 15 |
- m sync.Mutex |
|
| 17 |
+ mu sync.Mutex |
|
| 18 |
+ c sync.Cond // c.L lazily initialized to &p.mu |
|
| 19 |
+ b pipeBuffer |
|
| 20 |
+ err error // read error once empty. non-nil means closed. |
|
| 21 |
+ breakErr error // immediate read error (caller doesn't see rest of b) |
|
| 22 |
+ donec chan struct{} // closed on error
|
|
| 23 |
+ readFn func() // optional code to run in Read before error |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+type pipeBuffer interface {
|
|
| 27 |
+ Len() int |
|
| 28 |
+ io.Writer |
|
| 29 |
+ io.Reader |
|
| 16 | 30 |
} |
| 17 | 31 |
|
| 18 | 32 |
// Read waits until data is available and copies bytes |
| 19 | 33 |
// from the buffer into p. |
| 20 |
-func (r *pipe) Read(p []byte) (n int, err error) {
|
|
| 21 |
- r.c.L.Lock() |
|
| 22 |
- defer r.c.L.Unlock() |
|
| 23 |
- for r.b.Len() == 0 && !r.b.closed {
|
|
| 24 |
- r.c.Wait() |
|
| 34 |
+func (p *pipe) Read(d []byte) (n int, err error) {
|
|
| 35 |
+ p.mu.Lock() |
|
| 36 |
+ defer p.mu.Unlock() |
|
| 37 |
+ if p.c.L == nil {
|
|
| 38 |
+ p.c.L = &p.mu |
|
| 39 |
+ } |
|
| 40 |
+ for {
|
|
| 41 |
+ if p.breakErr != nil {
|
|
| 42 |
+ return 0, p.breakErr |
|
| 43 |
+ } |
|
| 44 |
+ if p.b.Len() > 0 {
|
|
| 45 |
+ return p.b.Read(d) |
|
| 46 |
+ } |
|
| 47 |
+ if p.err != nil {
|
|
| 48 |
+ if p.readFn != nil {
|
|
| 49 |
+ p.readFn() // e.g. copy trailers |
|
| 50 |
+ p.readFn = nil // not sticky like p.err |
|
| 51 |
+ } |
|
| 52 |
+ return 0, p.err |
|
| 53 |
+ } |
|
| 54 |
+ p.c.Wait() |
|
| 25 | 55 |
} |
| 26 |
- return r.b.Read(p) |
|
| 27 | 56 |
} |
| 28 | 57 |
|
| 58 |
+var errClosedPipeWrite = errors.New("write on closed buffer")
|
|
| 59 |
+ |
|
| 29 | 60 |
// Write copies bytes from p into the buffer and wakes a reader. |
| 30 | 61 |
// It is an error to write more data than the buffer can hold. |
| 31 |
-func (w *pipe) Write(p []byte) (n int, err error) {
|
|
| 32 |
- w.c.L.Lock() |
|
| 33 |
- defer w.c.L.Unlock() |
|
| 34 |
- defer w.c.Signal() |
|
| 35 |
- return w.b.Write(p) |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func (c *pipe) Close(err error) {
|
|
| 39 |
- c.c.L.Lock() |
|
| 40 |
- defer c.c.L.Unlock() |
|
| 41 |
- defer c.c.Signal() |
|
| 42 |
- c.b.Close(err) |
|
| 62 |
+func (p *pipe) Write(d []byte) (n int, err error) {
|
|
| 63 |
+ p.mu.Lock() |
|
| 64 |
+ defer p.mu.Unlock() |
|
| 65 |
+ if p.c.L == nil {
|
|
| 66 |
+ p.c.L = &p.mu |
|
| 67 |
+ } |
|
| 68 |
+ defer p.c.Signal() |
|
| 69 |
+ if p.err != nil {
|
|
| 70 |
+ return 0, errClosedPipeWrite |
|
| 71 |
+ } |
|
| 72 |
+ return p.b.Write(d) |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// CloseWithError causes the next Read (waking up a current blocked |
|
| 76 |
+// Read if needed) to return the provided err after all data has been |
|
| 77 |
+// read. |
|
| 78 |
+// |
|
| 79 |
+// The error must be non-nil. |
|
| 80 |
+func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) }
|
|
| 81 |
+ |
|
| 82 |
+// BreakWithError causes the next Read (waking up a current blocked |
|
| 83 |
+// Read if needed) to return the provided err immediately, without |
|
| 84 |
+// waiting for unread data. |
|
| 85 |
+func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) }
|
|
| 86 |
+ |
|
| 87 |
+// closeWithErrorAndCode is like CloseWithError but also sets some code to run |
|
| 88 |
+// in the caller's goroutine before returning the error. |
|
| 89 |
+func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) }
|
|
| 90 |
+ |
|
| 91 |
+func (p *pipe) closeWithError(dst *error, err error, fn func()) {
|
|
| 92 |
+ if err == nil {
|
|
| 93 |
+ panic("err must be non-nil")
|
|
| 94 |
+ } |
|
| 95 |
+ p.mu.Lock() |
|
| 96 |
+ defer p.mu.Unlock() |
|
| 97 |
+ if p.c.L == nil {
|
|
| 98 |
+ p.c.L = &p.mu |
|
| 99 |
+ } |
|
| 100 |
+ defer p.c.Signal() |
|
| 101 |
+ if *dst != nil {
|
|
| 102 |
+ // Already been done. |
|
| 103 |
+ return |
|
| 104 |
+ } |
|
| 105 |
+ p.readFn = fn |
|
| 106 |
+ *dst = err |
|
| 107 |
+ p.closeDoneLocked() |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+// requires p.mu be held. |
|
| 111 |
+func (p *pipe) closeDoneLocked() {
|
|
| 112 |
+ if p.donec == nil {
|
|
| 113 |
+ return |
|
| 114 |
+ } |
|
| 115 |
+ // Close if unclosed. This isn't racy since we always |
|
| 116 |
+ // hold p.mu while closing. |
|
| 117 |
+ select {
|
|
| 118 |
+ case <-p.donec: |
|
| 119 |
+ default: |
|
| 120 |
+ close(p.donec) |
|
| 121 |
+ } |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// Err returns the error (if any) first set by BreakWithError or CloseWithError. |
|
| 125 |
+func (p *pipe) Err() error {
|
|
| 126 |
+ p.mu.Lock() |
|
| 127 |
+ defer p.mu.Unlock() |
|
| 128 |
+ if p.breakErr != nil {
|
|
| 129 |
+ return p.breakErr |
|
| 130 |
+ } |
|
| 131 |
+ return p.err |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+// Done returns a channel which is closed if and when this pipe is closed |
|
| 135 |
+// with CloseWithError. |
|
| 136 |
+func (p *pipe) Done() <-chan struct{} {
|
|
| 137 |
+ p.mu.Lock() |
|
| 138 |
+ defer p.mu.Unlock() |
|
| 139 |
+ if p.donec == nil {
|
|
| 140 |
+ p.donec = make(chan struct{})
|
|
| 141 |
+ if p.err != nil || p.breakErr != nil {
|
|
| 142 |
+ // Already hit an error. |
|
| 143 |
+ p.closeDoneLocked() |
|
| 144 |
+ } |
|
| 145 |
+ } |
|
| 146 |
+ return p.donec |
|
| 43 | 147 |
} |
| ... | ... |
@@ -1,16 +1,13 @@ |
| 1 | 1 |
// Copyright 2014 The Go Authors. All rights reserved. |
| 2 | 2 |
// Use of this source code is governed by a BSD-style |
| 3 | 3 |
// license that can be found in the LICENSE file. |
| 4 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 5 |
-// Licensed under the same terms as Go itself: |
|
| 6 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 7 | 4 |
|
| 8 | 5 |
// TODO: replace all <-sc.doneServing with reads from the stream's cw |
| 9 | 6 |
// instead, and make sure that on close we close all open |
| 10 | 7 |
// streams. then remove doneServing? |
| 11 | 8 |
|
| 12 |
-// TODO: finish GOAWAY support. Consider each incoming frame type and |
|
| 13 |
-// whether it should be ignored during a shutdown race. |
|
| 9 |
+// TODO: re-audit GOAWAY support. Consider each incoming frame type and |
|
| 10 |
+// whether it should be ignored during graceful shutdown. |
|
| 14 | 11 |
|
| 15 | 12 |
// TODO: disconnect idle clients. GFE seems to do 4 minutes. make |
| 16 | 13 |
// configurable? or maximum number of idle clients and remove the |
| ... | ... |
@@ -49,7 +46,11 @@ import ( |
| 49 | 49 |
"log" |
| 50 | 50 |
"net" |
| 51 | 51 |
"net/http" |
| 52 |
+ "net/textproto" |
|
| 52 | 53 |
"net/url" |
| 54 |
+ "os" |
|
| 55 |
+ "reflect" |
|
| 56 |
+ "runtime" |
|
| 53 | 57 |
"strconv" |
| 54 | 58 |
"strings" |
| 55 | 59 |
"sync" |
| ... | ... |
@@ -68,7 +69,8 @@ const ( |
| 68 | 68 |
var ( |
| 69 | 69 |
errClientDisconnected = errors.New("client disconnected")
|
| 70 | 70 |
errClosedBody = errors.New("body closed by handler")
|
| 71 |
- errStreamBroken = errors.New("http2: stream broken")
|
|
| 71 |
+ errHandlerComplete = errors.New("http2: request body closed due to handler exiting")
|
|
| 72 |
+ errStreamClosed = errors.New("http2: stream closed")
|
|
| 72 | 73 |
) |
| 73 | 74 |
|
| 74 | 75 |
var responseWriterStatePool = sync.Pool{
|
| ... | ... |
@@ -133,12 +135,33 @@ func (s *Server) maxConcurrentStreams() uint32 {
|
| 133 | 133 |
// The configuration conf may be nil. |
| 134 | 134 |
// |
| 135 | 135 |
// ConfigureServer must be called before s begins serving. |
| 136 |
-func ConfigureServer(s *http.Server, conf *Server) {
|
|
| 136 |
+func ConfigureServer(s *http.Server, conf *Server) error {
|
|
| 137 | 137 |
if conf == nil {
|
| 138 | 138 |
conf = new(Server) |
| 139 | 139 |
} |
| 140 |
+ |
|
| 140 | 141 |
if s.TLSConfig == nil {
|
| 141 | 142 |
s.TLSConfig = new(tls.Config) |
| 143 |
+ } else if s.TLSConfig.CipherSuites != nil {
|
|
| 144 |
+ // If they already provided a CipherSuite list, return |
|
| 145 |
+ // an error if it has a bad order or is missing |
|
| 146 |
+ // ECDHE_RSA_WITH_AES_128_GCM_SHA256. |
|
| 147 |
+ const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
|
| 148 |
+ haveRequired := false |
|
| 149 |
+ sawBad := false |
|
| 150 |
+ for i, cs := range s.TLSConfig.CipherSuites {
|
|
| 151 |
+ if cs == requiredCipher {
|
|
| 152 |
+ haveRequired = true |
|
| 153 |
+ } |
|
| 154 |
+ if isBadCipher(cs) {
|
|
| 155 |
+ sawBad = true |
|
| 156 |
+ } else if sawBad {
|
|
| 157 |
+ return fmt.Errorf("http2: TLSConfig.CipherSuites index %d contains an HTTP/2-approved cipher suite (%#04x), but it comes after unapproved cipher suites. With this configuration, clients that don't support previous, approved cipher suites may be given an unapproved one and reject the connection.", i, cs)
|
|
| 158 |
+ } |
|
| 159 |
+ } |
|
| 160 |
+ if !haveRequired {
|
|
| 161 |
+ return fmt.Errorf("http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
|
|
| 162 |
+ } |
|
| 142 | 163 |
} |
| 143 | 164 |
|
| 144 | 165 |
// Note: not setting MinVersion to tls.VersionTLS12, |
| ... | ... |
@@ -148,22 +171,7 @@ func ConfigureServer(s *http.Server, conf *Server) {
|
| 148 | 148 |
// during next-proto selection, but using TLS <1.2 with |
| 149 | 149 |
// HTTP/2 is still the client's bug. |
| 150 | 150 |
|
| 151 |
- // Be sure we advertise tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
|
| 152 |
- // at least. |
|
| 153 |
- // TODO: enable PreferServerCipherSuites? |
|
| 154 |
- if s.TLSConfig.CipherSuites != nil {
|
|
| 155 |
- const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
|
| 156 |
- haveRequired := false |
|
| 157 |
- for _, v := range s.TLSConfig.CipherSuites {
|
|
| 158 |
- if v == requiredCipher {
|
|
| 159 |
- haveRequired = true |
|
| 160 |
- break |
|
| 161 |
- } |
|
| 162 |
- } |
|
| 163 |
- if !haveRequired {
|
|
| 164 |
- s.TLSConfig.CipherSuites = append(s.TLSConfig.CipherSuites, requiredCipher) |
|
| 165 |
- } |
|
| 166 |
- } |
|
| 151 |
+ s.TLSConfig.PreferServerCipherSuites = true |
|
| 167 | 152 |
|
| 168 | 153 |
haveNPN := false |
| 169 | 154 |
for _, p := range s.TLSConfig.NextProtos {
|
| ... | ... |
@@ -186,28 +194,76 @@ func ConfigureServer(s *http.Server, conf *Server) {
|
| 186 | 186 |
if testHookOnConn != nil {
|
| 187 | 187 |
testHookOnConn() |
| 188 | 188 |
} |
| 189 |
- conf.handleConn(hs, c, h) |
|
| 189 |
+ conf.ServeConn(c, &ServeConnOpts{
|
|
| 190 |
+ Handler: h, |
|
| 191 |
+ BaseConfig: hs, |
|
| 192 |
+ }) |
|
| 190 | 193 |
} |
| 191 | 194 |
s.TLSNextProto[NextProtoTLS] = protoHandler |
| 192 | 195 |
s.TLSNextProto["h2-14"] = protoHandler // temporary; see above. |
| 196 |
+ return nil |
|
| 193 | 197 |
} |
| 194 | 198 |
|
| 195 |
-func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
|
|
| 199 |
+// ServeConnOpts are options for the Server.ServeConn method. |
|
| 200 |
+type ServeConnOpts struct {
|
|
| 201 |
+ // BaseConfig optionally sets the base configuration |
|
| 202 |
+ // for values. If nil, defaults are used. |
|
| 203 |
+ BaseConfig *http.Server |
|
| 204 |
+ |
|
| 205 |
+ // Handler specifies which handler to use for processing |
|
| 206 |
+ // requests. If nil, BaseConfig.Handler is used. If BaseConfig |
|
| 207 |
+ // or BaseConfig.Handler is nil, http.DefaultServeMux is used. |
|
| 208 |
+ Handler http.Handler |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+func (o *ServeConnOpts) baseConfig() *http.Server {
|
|
| 212 |
+ if o != nil && o.BaseConfig != nil {
|
|
| 213 |
+ return o.BaseConfig |
|
| 214 |
+ } |
|
| 215 |
+ return new(http.Server) |
|
| 216 |
+} |
|
| 217 |
+ |
|
| 218 |
+func (o *ServeConnOpts) handler() http.Handler {
|
|
| 219 |
+ if o != nil {
|
|
| 220 |
+ if o.Handler != nil {
|
|
| 221 |
+ return o.Handler |
|
| 222 |
+ } |
|
| 223 |
+ if o.BaseConfig != nil && o.BaseConfig.Handler != nil {
|
|
| 224 |
+ return o.BaseConfig.Handler |
|
| 225 |
+ } |
|
| 226 |
+ } |
|
| 227 |
+ return http.DefaultServeMux |
|
| 228 |
+} |
|
| 229 |
+ |
|
| 230 |
+// ServeConn serves HTTP/2 requests on the provided connection and |
|
| 231 |
+// blocks until the connection is no longer readable. |
|
| 232 |
+// |
|
| 233 |
+// ServeConn starts speaking HTTP/2 assuming that c has not had any |
|
| 234 |
+// reads or writes. It writes its initial settings frame and expects |
|
| 235 |
+// to be able to read the preface and settings frame from the |
|
| 236 |
+// client. If c has a ConnectionState method like a *tls.Conn, the |
|
| 237 |
+// ConnectionState is used to verify the TLS ciphersuite and to set |
|
| 238 |
+// the Request.TLS field in Handlers. |
|
| 239 |
+// |
|
| 240 |
+// ServeConn does not support h2c by itself. Any h2c support must be |
|
| 241 |
+// implemented in terms of providing a suitably-behaving net.Conn. |
|
| 242 |
+// |
|
| 243 |
+// The opts parameter is optional. If nil, default values are used. |
|
| 244 |
+func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|
| 196 | 245 |
sc := &serverConn{
|
| 197 |
- srv: srv, |
|
| 198 |
- hs: hs, |
|
| 246 |
+ srv: s, |
|
| 247 |
+ hs: opts.baseConfig(), |
|
| 199 | 248 |
conn: c, |
| 200 | 249 |
remoteAddrStr: c.RemoteAddr().String(), |
| 201 | 250 |
bw: newBufferedWriter(c), |
| 202 |
- handler: h, |
|
| 251 |
+ handler: opts.handler(), |
|
| 203 | 252 |
streams: make(map[uint32]*stream), |
| 204 |
- readFrameCh: make(chan frameAndGate), |
|
| 205 |
- readFrameErrCh: make(chan error, 1), // must be buffered for 1 |
|
| 253 |
+ readFrameCh: make(chan readFrameResult), |
|
| 206 | 254 |
wantWriteFrameCh: make(chan frameWriteMsg, 8), |
| 207 |
- wroteFrameCh: make(chan struct{}, 1), // buffered; one send in reading goroutine
|
|
| 208 |
- bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way |
|
| 255 |
+ wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync |
|
| 256 |
+ bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way |
|
| 209 | 257 |
doneServing: make(chan struct{}),
|
| 210 |
- advMaxStreams: srv.maxConcurrentStreams(), |
|
| 258 |
+ advMaxStreams: s.maxConcurrentStreams(), |
|
| 211 | 259 |
writeSched: writeScheduler{
|
| 212 | 260 |
maxFrameSize: initialMaxFrameSize, |
| 213 | 261 |
}, |
| ... | ... |
@@ -219,13 +275,14 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
|
| 219 | 219 |
sc.flow.add(initialWindowSize) |
| 220 | 220 |
sc.inflow.add(initialWindowSize) |
| 221 | 221 |
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) |
| 222 |
- sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, sc.onNewHeaderField) |
|
| 223 | 222 |
|
| 224 | 223 |
fr := NewFramer(sc.bw, c) |
| 225 |
- fr.SetMaxReadFrameSize(srv.maxReadFrameSize()) |
|
| 224 |
+ fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil) |
|
| 225 |
+ fr.MaxHeaderListSize = sc.maxHeaderListSize() |
|
| 226 |
+ fr.SetMaxReadFrameSize(s.maxReadFrameSize()) |
|
| 226 | 227 |
sc.framer = fr |
| 227 | 228 |
|
| 228 |
- if tc, ok := c.(*tls.Conn); ok {
|
|
| 229 |
+ if tc, ok := c.(connectionStater); ok {
|
|
| 229 | 230 |
sc.tlsState = new(tls.ConnectionState) |
| 230 | 231 |
*sc.tlsState = tc.ConnectionState() |
| 231 | 232 |
// 9.2 Use of TLS Features |
| ... | ... |
@@ -255,7 +312,7 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
|
| 255 | 255 |
// So for now, do nothing here again. |
| 256 | 256 |
} |
| 257 | 257 |
|
| 258 |
- if !srv.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
|
|
| 258 |
+ if !s.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
|
|
| 259 | 259 |
// "Endpoints MAY choose to generate a connection error |
| 260 | 260 |
// (Section 5.4.1) of type INADEQUATE_SECURITY if one of |
| 261 | 261 |
// the prohibited cipher suites are negotiated." |
| ... | ... |
@@ -302,23 +359,13 @@ func isBadCipher(cipher uint16) bool {
|
| 302 | 302 |
} |
| 303 | 303 |
|
| 304 | 304 |
func (sc *serverConn) rejectConn(err ErrCode, debug string) {
|
| 305 |
- log.Printf("REJECTING conn: %v, %s", err, debug)
|
|
| 305 |
+ sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
|
|
| 306 | 306 |
// ignoring errors. hanging up anyway. |
| 307 | 307 |
sc.framer.WriteGoAway(0, err, []byte(debug)) |
| 308 | 308 |
sc.bw.Flush() |
| 309 | 309 |
sc.conn.Close() |
| 310 | 310 |
} |
| 311 | 311 |
|
| 312 |
-// frameAndGates coordinates the readFrames and serve |
|
| 313 |
-// goroutines. Because the Framer interface only permits the most |
|
| 314 |
-// recently-read Frame from being accessed, the readFrames goroutine |
|
| 315 |
-// blocks until it has a frame, passes it to serve, and then waits for |
|
| 316 |
-// serve to be done with it before reading the next one. |
|
| 317 |
-type frameAndGate struct {
|
|
| 318 |
- f Frame |
|
| 319 |
- g gate |
|
| 320 |
-} |
|
| 321 |
- |
|
| 322 | 312 |
type serverConn struct {
|
| 323 | 313 |
// Immutable: |
| 324 | 314 |
srv *Server |
| ... | ... |
@@ -327,17 +374,15 @@ type serverConn struct {
|
| 327 | 327 |
bw *bufferedWriter // writing to conn |
| 328 | 328 |
handler http.Handler |
| 329 | 329 |
framer *Framer |
| 330 |
- hpackDecoder *hpack.Decoder |
|
| 331 |
- doneServing chan struct{} // closed when serverConn.serve ends
|
|
| 332 |
- readFrameCh chan frameAndGate // written by serverConn.readFrames |
|
| 333 |
- readFrameErrCh chan error |
|
| 334 |
- wantWriteFrameCh chan frameWriteMsg // from handlers -> serve |
|
| 335 |
- wroteFrameCh chan struct{} // from writeFrameAsync -> serve, tickles more frame writes
|
|
| 336 |
- bodyReadCh chan bodyReadMsg // from handlers -> serve |
|
| 337 |
- testHookCh chan func() // code to run on the serve loop |
|
| 338 |
- flow flow // conn-wide (not stream-specific) outbound flow control |
|
| 339 |
- inflow flow // conn-wide inbound flow control |
|
| 340 |
- tlsState *tls.ConnectionState // shared by all handlers, like net/http |
|
| 330 |
+ doneServing chan struct{} // closed when serverConn.serve ends
|
|
| 331 |
+ readFrameCh chan readFrameResult // written by serverConn.readFrames |
|
| 332 |
+ wantWriteFrameCh chan frameWriteMsg // from handlers -> serve |
|
| 333 |
+ wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes |
|
| 334 |
+ bodyReadCh chan bodyReadMsg // from handlers -> serve |
|
| 335 |
+ testHookCh chan func(int) // code to run on the serve loop |
|
| 336 |
+ flow flow // conn-wide (not stream-specific) outbound flow control |
|
| 337 |
+ inflow flow // conn-wide inbound flow control |
|
| 338 |
+ tlsState *tls.ConnectionState // shared by all handlers, like net/http |
|
| 341 | 339 |
remoteAddrStr string |
| 342 | 340 |
|
| 343 | 341 |
// Everything following is owned by the serve loop; use serveG.check(): |
| ... | ... |
@@ -353,9 +398,8 @@ type serverConn struct {
|
| 353 | 353 |
streams map[uint32]*stream |
| 354 | 354 |
initialWindowSize int32 |
| 355 | 355 |
headerTableSize uint32 |
| 356 |
- maxHeaderListSize uint32 // zero means unknown (default) |
|
| 356 |
+ peerMaxHeaderListSize uint32 // zero means unknown (default) |
|
| 357 | 357 |
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case |
| 358 |
- req requestParam // non-zero while reading request headers |
|
| 359 | 358 |
writingFrame bool // started write goroutine but haven't heard back on wroteFrameCh |
| 360 | 359 |
needsFrameFlush bool // last frame write wasn't a flush |
| 361 | 360 |
writeSched writeScheduler |
| ... | ... |
@@ -364,24 +408,23 @@ type serverConn struct {
|
| 364 | 364 |
goAwayCode ErrCode |
| 365 | 365 |
shutdownTimerCh <-chan time.Time // nil until used |
| 366 | 366 |
shutdownTimer *time.Timer // nil until used |
| 367 |
+ freeRequestBodyBuf []byte // if non-nil, a free initialWindowSize buffer for getRequestBodyBuf |
|
| 367 | 368 |
|
| 368 | 369 |
// Owned by the writeFrameAsync goroutine: |
| 369 | 370 |
headerWriteBuf bytes.Buffer |
| 370 | 371 |
hpackEncoder *hpack.Encoder |
| 371 | 372 |
} |
| 372 | 373 |
|
| 373 |
-// requestParam is the state of the next request, initialized over |
|
| 374 |
-// potentially several frames HEADERS + zero or more CONTINUATION |
|
| 375 |
-// frames. |
|
| 376 |
-type requestParam struct {
|
|
| 377 |
- // stream is non-nil if we're reading (HEADER or CONTINUATION) |
|
| 378 |
- // frames for a request (but not DATA). |
|
| 379 |
- stream *stream |
|
| 380 |
- header http.Header |
|
| 381 |
- method, path string |
|
| 382 |
- scheme, authority string |
|
| 383 |
- sawRegularHeader bool // saw a non-pseudo header already |
|
| 384 |
- invalidHeader bool // an invalid header was seen |
|
| 374 |
+func (sc *serverConn) maxHeaderListSize() uint32 {
|
|
| 375 |
+ n := sc.hs.MaxHeaderBytes |
|
| 376 |
+ if n <= 0 {
|
|
| 377 |
+ n = http.DefaultMaxHeaderBytes |
|
| 378 |
+ } |
|
| 379 |
+ // http2's count is in a slightly different unit and includes 32 bytes per pair. |
|
| 380 |
+ // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. |
|
| 381 |
+ const perFieldOverhead = 32 // per http2 spec |
|
| 382 |
+ const typicalHeaders = 10 // conservative |
|
| 383 |
+ return uint32(n + typicalHeaders*perFieldOverhead) |
|
| 385 | 384 |
} |
| 386 | 385 |
|
| 387 | 386 |
// stream represents a stream. This is the minimal metadata needed by |
| ... | ... |
@@ -393,20 +436,27 @@ type requestParam struct {
|
| 393 | 393 |
// responseWriter's state field. |
| 394 | 394 |
type stream struct {
|
| 395 | 395 |
// immutable: |
| 396 |
+ sc *serverConn |
|
| 396 | 397 |
id uint32 |
| 397 | 398 |
body *pipe // non-nil if expecting DATA frames |
| 398 | 399 |
cw closeWaiter // closed wait stream transitions to closed state |
| 399 | 400 |
|
| 400 | 401 |
// owned by serverConn's serve loop: |
| 401 |
- bodyBytes int64 // body bytes seen so far |
|
| 402 |
- declBodyBytes int64 // or -1 if undeclared |
|
| 403 |
- flow flow // limits writing from Handler to client |
|
| 404 |
- inflow flow // what the client is allowed to POST/etc to us |
|
| 405 |
- parent *stream // or nil |
|
| 406 |
- weight uint8 |
|
| 407 |
- state streamState |
|
| 408 |
- sentReset bool // only true once detached from streams map |
|
| 409 |
- gotReset bool // only true once detacted from streams map |
|
| 402 |
+ bodyBytes int64 // body bytes seen so far |
|
| 403 |
+ declBodyBytes int64 // or -1 if undeclared |
|
| 404 |
+ flow flow // limits writing from Handler to client |
|
| 405 |
+ inflow flow // what the client is allowed to POST/etc to us |
|
| 406 |
+ parent *stream // or nil |
|
| 407 |
+ numTrailerValues int64 |
|
| 408 |
+ weight uint8 |
|
| 409 |
+ state streamState |
|
| 410 |
+ sentReset bool // only true once detached from streams map |
|
| 411 |
+ gotReset bool // only true once detacted from streams map |
|
| 412 |
+ gotTrailerHeader bool // HEADER frame for trailers was seen |
|
| 413 |
+ reqBuf []byte |
|
| 414 |
+ |
|
| 415 |
+ trailer http.Header // accumulated trailers |
|
| 416 |
+ reqTrailer http.Header // handler's Request.Trailer |
|
| 410 | 417 |
} |
| 411 | 418 |
|
| 412 | 419 |
func (sc *serverConn) Framer() *Framer { return sc.framer }
|
| ... | ... |
@@ -434,6 +484,15 @@ func (sc *serverConn) state(streamID uint32) (streamState, *stream) {
|
| 434 | 434 |
return stateIdle, nil |
| 435 | 435 |
} |
| 436 | 436 |
|
| 437 |
+// setConnState calls the net/http ConnState hook for this connection, if configured. |
|
| 438 |
+// Note that the net/http package does StateNew and StateClosed for us. |
|
| 439 |
+// There is currently no plan for StateHijacked or hijacking HTTP/2 connections. |
|
| 440 |
+func (sc *serverConn) setConnState(state http.ConnState) {
|
|
| 441 |
+ if sc.hs.ConnState != nil {
|
|
| 442 |
+ sc.hs.ConnState(sc.conn, state) |
|
| 443 |
+ } |
|
| 444 |
+} |
|
| 445 |
+ |
|
| 437 | 446 |
func (sc *serverConn) vlogf(format string, args ...interface{}) {
|
| 438 | 447 |
if VerboseLogs {
|
| 439 | 448 |
sc.logf(format, args...) |
| ... | ... |
@@ -448,12 +507,55 @@ func (sc *serverConn) logf(format string, args ...interface{}) {
|
| 448 | 448 |
} |
| 449 | 449 |
} |
| 450 | 450 |
|
| 451 |
+// errno returns v's underlying uintptr, else 0. |
|
| 452 |
+// |
|
| 453 |
+// TODO: remove this helper function once http2 can use build |
|
| 454 |
+// tags. See comment in isClosedConnError. |
|
| 455 |
+func errno(v error) uintptr {
|
|
| 456 |
+ if rv := reflect.ValueOf(v); rv.Kind() == reflect.Uintptr {
|
|
| 457 |
+ return uintptr(rv.Uint()) |
|
| 458 |
+ } |
|
| 459 |
+ return 0 |
|
| 460 |
+} |
|
| 461 |
+ |
|
| 462 |
+// isClosedConnError reports whether err is an error from use of a closed |
|
| 463 |
+// network connection. |
|
| 464 |
+func isClosedConnError(err error) bool {
|
|
| 465 |
+ if err == nil {
|
|
| 466 |
+ return false |
|
| 467 |
+ } |
|
| 468 |
+ |
|
| 469 |
+ // TODO: remove this string search and be more like the Windows |
|
| 470 |
+ // case below. That might involve modifying the standard library |
|
| 471 |
+ // to return better error types. |
|
| 472 |
+ str := err.Error() |
|
| 473 |
+ if strings.Contains(str, "use of closed network connection") {
|
|
| 474 |
+ return true |
|
| 475 |
+ } |
|
| 476 |
+ |
|
| 477 |
+ // TODO(bradfitz): x/tools/cmd/bundle doesn't really support |
|
| 478 |
+ // build tags, so I can't make an http2_windows.go file with |
|
| 479 |
+ // Windows-specific stuff. Fix that and move this, once we |
|
| 480 |
+ // have a way to bundle this into std's net/http somehow. |
|
| 481 |
+ if runtime.GOOS == "windows" {
|
|
| 482 |
+ if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
|
|
| 483 |
+ if se, ok := oe.Err.(*os.SyscallError); ok && se.Syscall == "wsarecv" {
|
|
| 484 |
+ const WSAECONNABORTED = 10053 |
|
| 485 |
+ const WSAECONNRESET = 10054 |
|
| 486 |
+ if n := errno(se.Err); n == WSAECONNRESET || n == WSAECONNABORTED {
|
|
| 487 |
+ return true |
|
| 488 |
+ } |
|
| 489 |
+ } |
|
| 490 |
+ } |
|
| 491 |
+ } |
|
| 492 |
+ return false |
|
| 493 |
+} |
|
| 494 |
+ |
|
| 451 | 495 |
func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
|
| 452 | 496 |
if err == nil {
|
| 453 | 497 |
return |
| 454 | 498 |
} |
| 455 |
- str := err.Error() |
|
| 456 |
- if err == io.EOF || strings.Contains(str, "use of closed network connection") {
|
|
| 499 |
+ if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) {
|
|
| 457 | 500 |
// Boring, expected errors. |
| 458 | 501 |
sc.vlogf(format, args...) |
| 459 | 502 |
} else {
|
| ... | ... |
@@ -461,57 +563,6 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
|
| 461 | 461 |
} |
| 462 | 462 |
} |
| 463 | 463 |
|
| 464 |
-func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
|
|
| 465 |
- sc.serveG.check() |
|
| 466 |
- sc.vlogf("got header field %+v", f)
|
|
| 467 |
- switch {
|
|
| 468 |
- case !validHeader(f.Name): |
|
| 469 |
- sc.req.invalidHeader = true |
|
| 470 |
- case strings.HasPrefix(f.Name, ":"): |
|
| 471 |
- if sc.req.sawRegularHeader {
|
|
| 472 |
- sc.logf("pseudo-header after regular header")
|
|
| 473 |
- sc.req.invalidHeader = true |
|
| 474 |
- return |
|
| 475 |
- } |
|
| 476 |
- var dst *string |
|
| 477 |
- switch f.Name {
|
|
| 478 |
- case ":method": |
|
| 479 |
- dst = &sc.req.method |
|
| 480 |
- case ":path": |
|
| 481 |
- dst = &sc.req.path |
|
| 482 |
- case ":scheme": |
|
| 483 |
- dst = &sc.req.scheme |
|
| 484 |
- case ":authority": |
|
| 485 |
- dst = &sc.req.authority |
|
| 486 |
- default: |
|
| 487 |
- // 8.1.2.1 Pseudo-Header Fields |
|
| 488 |
- // "Endpoints MUST treat a request or response |
|
| 489 |
- // that contains undefined or invalid |
|
| 490 |
- // pseudo-header fields as malformed (Section |
|
| 491 |
- // 8.1.2.6)." |
|
| 492 |
- sc.logf("invalid pseudo-header %q", f.Name)
|
|
| 493 |
- sc.req.invalidHeader = true |
|
| 494 |
- return |
|
| 495 |
- } |
|
| 496 |
- if *dst != "" {
|
|
| 497 |
- sc.logf("duplicate pseudo-header %q sent", f.Name)
|
|
| 498 |
- sc.req.invalidHeader = true |
|
| 499 |
- return |
|
| 500 |
- } |
|
| 501 |
- *dst = f.Value |
|
| 502 |
- case f.Name == "cookie": |
|
| 503 |
- sc.req.sawRegularHeader = true |
|
| 504 |
- if s, ok := sc.req.header["Cookie"]; ok && len(s) == 1 {
|
|
| 505 |
- s[0] = s[0] + "; " + f.Value |
|
| 506 |
- } else {
|
|
| 507 |
- sc.req.header.Add("Cookie", f.Value)
|
|
| 508 |
- } |
|
| 509 |
- default: |
|
| 510 |
- sc.req.sawRegularHeader = true |
|
| 511 |
- sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value) |
|
| 512 |
- } |
|
| 513 |
-} |
|
| 514 |
- |
|
| 515 | 464 |
func (sc *serverConn) canonicalHeader(v string) string {
|
| 516 | 465 |
sc.serveG.check() |
| 517 | 466 |
cv, ok := commonCanonHeader[v] |
| ... | ... |
@@ -530,41 +581,54 @@ func (sc *serverConn) canonicalHeader(v string) string {
|
| 530 | 530 |
return cv |
| 531 | 531 |
} |
| 532 | 532 |
|
| 533 |
+type readFrameResult struct {
|
|
| 534 |
+ f Frame // valid until readMore is called |
|
| 535 |
+ err error |
|
| 536 |
+ |
|
| 537 |
+ // readMore should be called once the consumer no longer needs or |
|
| 538 |
+ // retains f. After readMore, f is invalid and more frames can be |
|
| 539 |
+ // read. |
|
| 540 |
+ readMore func() |
|
| 541 |
+} |
|
| 542 |
+ |
|
| 533 | 543 |
// readFrames is the loop that reads incoming frames. |
| 544 |
+// It takes care to only read one frame at a time, blocking until the |
|
| 545 |
+// consumer is done with the frame. |
|
| 534 | 546 |
// It's run on its own goroutine. |
| 535 | 547 |
func (sc *serverConn) readFrames() {
|
| 536 |
- g := make(gate, 1) |
|
| 548 |
+ gate := make(gate) |
|
| 549 |
+ gateDone := gate.Done |
|
| 537 | 550 |
for {
|
| 538 | 551 |
f, err := sc.framer.ReadFrame() |
| 539 |
- if err != nil {
|
|
| 540 |
- sc.readFrameErrCh <- err |
|
| 541 |
- close(sc.readFrameCh) |
|
| 552 |
+ select {
|
|
| 553 |
+ case sc.readFrameCh <- readFrameResult{f, err, gateDone}:
|
|
| 554 |
+ case <-sc.doneServing: |
|
| 555 |
+ return |
|
| 556 |
+ } |
|
| 557 |
+ select {
|
|
| 558 |
+ case <-gate: |
|
| 559 |
+ case <-sc.doneServing: |
|
| 560 |
+ return |
|
| 561 |
+ } |
|
| 562 |
+ if terminalReadFrameError(err) {
|
|
| 542 | 563 |
return |
| 543 | 564 |
} |
| 544 |
- sc.readFrameCh <- frameAndGate{f, g}
|
|
| 545 |
- // We can't read another frame until this one is |
|
| 546 |
- // processed, as the ReadFrame interface doesn't copy |
|
| 547 |
- // memory. The Frame accessor methods access the last |
|
| 548 |
- // frame's (shared) buffer. So we wait for the |
|
| 549 |
- // serve goroutine to tell us it's done: |
|
| 550 |
- g.Wait() |
|
| 551 | 565 |
} |
| 552 | 566 |
} |
| 553 | 567 |
|
| 568 |
+// frameWriteResult is the message passed from writeFrameAsync to the serve goroutine. |
|
| 569 |
+type frameWriteResult struct {
|
|
| 570 |
+ wm frameWriteMsg // what was written (or attempted) |
|
| 571 |
+ err error // result of the writeFrame call |
|
| 572 |
+} |
|
| 573 |
+ |
|
| 554 | 574 |
// writeFrameAsync runs in its own goroutine and writes a single frame |
| 555 | 575 |
// and then reports when it's done. |
| 556 | 576 |
// At most one goroutine can be running writeFrameAsync at a time per |
| 557 | 577 |
// serverConn. |
| 558 | 578 |
func (sc *serverConn) writeFrameAsync(wm frameWriteMsg) {
|
| 559 | 579 |
err := wm.write.writeFrame(sc) |
| 560 |
- if ch := wm.done; ch != nil {
|
|
| 561 |
- select {
|
|
| 562 |
- case ch <- err: |
|
| 563 |
- default: |
|
| 564 |
- panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wm.write))
|
|
| 565 |
- } |
|
| 566 |
- } |
|
| 567 |
- sc.wroteFrameCh <- struct{}{} // tickle frame selection scheduler
|
|
| 580 |
+ sc.wroteFrameCh <- frameWriteResult{wm, err}
|
|
| 568 | 581 |
} |
| 569 | 582 |
|
| 570 | 583 |
func (sc *serverConn) closeAllStreamsOnConnClose() {
|
| ... | ... |
@@ -582,6 +646,7 @@ func (sc *serverConn) stopShutdownTimer() {
|
| 582 | 582 |
} |
| 583 | 583 |
|
| 584 | 584 |
func (sc *serverConn) notePanic() {
|
| 585 |
+ // Note: this is for serverConn.serve panicking, not http.Handler code. |
|
| 585 | 586 |
if testHookOnPanicMu != nil {
|
| 586 | 587 |
testHookOnPanicMu.Lock() |
| 587 | 588 |
defer testHookOnPanicMu.Unlock() |
| ... | ... |
@@ -603,12 +668,15 @@ func (sc *serverConn) serve() {
|
| 603 | 603 |
defer sc.stopShutdownTimer() |
| 604 | 604 |
defer close(sc.doneServing) // unblocks handlers trying to send |
| 605 | 605 |
|
| 606 |
- sc.vlogf("HTTP/2 connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
|
|
| 606 |
+ if VerboseLogs {
|
|
| 607 |
+ sc.vlogf("http2: server connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
|
|
| 608 |
+ } |
|
| 607 | 609 |
|
| 608 | 610 |
sc.writeFrame(frameWriteMsg{
|
| 609 | 611 |
write: writeSettings{
|
| 610 | 612 |
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
|
| 611 | 613 |
{SettingMaxConcurrentStreams, sc.advMaxStreams},
|
| 614 |
+ {SettingMaxHeaderListSize, sc.maxHeaderListSize()},
|
|
| 612 | 615 |
|
| 613 | 616 |
// TODO: more actual settings, notably |
| 614 | 617 |
// SettingInitialWindowSize, but then we also |
| ... | ... |
@@ -619,30 +687,32 @@ func (sc *serverConn) serve() {
|
| 619 | 619 |
sc.unackedSettings++ |
| 620 | 620 |
|
| 621 | 621 |
if err := sc.readPreface(); err != nil {
|
| 622 |
- sc.condlogf(err, "error reading preface from client %v: %v", sc.conn.RemoteAddr(), err) |
|
| 622 |
+ sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err) |
|
| 623 | 623 |
return |
| 624 | 624 |
} |
| 625 |
+ // Now that we've got the preface, get us out of the |
|
| 626 |
+ // "StateNew" state. We can't go directly to idle, though. |
|
| 627 |
+ // Active means we read some data and anticipate a request. We'll |
|
| 628 |
+ // do another Active when we get a HEADERS frame. |
|
| 629 |
+ sc.setConnState(http.StateActive) |
|
| 630 |
+ sc.setConnState(http.StateIdle) |
|
| 625 | 631 |
|
| 626 | 632 |
go sc.readFrames() // closed by defer sc.conn.Close above |
| 627 | 633 |
|
| 628 | 634 |
settingsTimer := time.NewTimer(firstSettingsTimeout) |
| 635 |
+ loopNum := 0 |
|
| 629 | 636 |
for {
|
| 637 |
+ loopNum++ |
|
| 630 | 638 |
select {
|
| 631 | 639 |
case wm := <-sc.wantWriteFrameCh: |
| 632 | 640 |
sc.writeFrame(wm) |
| 633 |
- case <-sc.wroteFrameCh: |
|
| 634 |
- if sc.writingFrame != true {
|
|
| 635 |
- panic("internal error: expected to be already writing a frame")
|
|
| 636 |
- } |
|
| 637 |
- sc.writingFrame = false |
|
| 638 |
- sc.scheduleFrameWrite() |
|
| 639 |
- case fg, ok := <-sc.readFrameCh: |
|
| 640 |
- if !ok {
|
|
| 641 |
- sc.readFrameCh = nil |
|
| 642 |
- } |
|
| 643 |
- if !sc.processFrameFromReader(fg, ok) {
|
|
| 641 |
+ case res := <-sc.wroteFrameCh: |
|
| 642 |
+ sc.wroteFrame(res) |
|
| 643 |
+ case res := <-sc.readFrameCh: |
|
| 644 |
+ if !sc.processFrameFromReader(res) {
|
|
| 644 | 645 |
return |
| 645 | 646 |
} |
| 647 |
+ res.readMore() |
|
| 646 | 648 |
if settingsTimer.C != nil {
|
| 647 | 649 |
settingsTimer.Stop() |
| 648 | 650 |
settingsTimer.C = nil |
| ... | ... |
@@ -656,7 +726,7 @@ func (sc *serverConn) serve() {
|
| 656 | 656 |
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
|
| 657 | 657 |
return |
| 658 | 658 |
case fn := <-sc.testHookCh: |
| 659 |
- fn() |
|
| 659 |
+ fn(loopNum) |
|
| 660 | 660 |
} |
| 661 | 661 |
} |
| 662 | 662 |
} |
| ... | ... |
@@ -683,38 +753,62 @@ func (sc *serverConn) readPreface() error {
|
| 683 | 683 |
return errors.New("timeout waiting for client preface")
|
| 684 | 684 |
case err := <-errc: |
| 685 | 685 |
if err == nil {
|
| 686 |
- sc.vlogf("client %v said hello", sc.conn.RemoteAddr())
|
|
| 686 |
+ if VerboseLogs {
|
|
| 687 |
+ sc.vlogf("http2: server: client %v said hello", sc.conn.RemoteAddr())
|
|
| 688 |
+ } |
|
| 687 | 689 |
} |
| 688 | 690 |
return err |
| 689 | 691 |
} |
| 690 | 692 |
} |
| 691 | 693 |
|
| 692 |
-// writeDataFromHandler writes the data described in req to stream.id. |
|
| 693 |
-// |
|
| 694 |
-// The provided ch is used to avoid allocating new channels for each |
|
| 695 |
-// write operation. It's expected that the caller reuses writeData and ch |
|
| 696 |
-// over time. |
|
| 697 |
-// |
|
| 698 |
-// The flow control currently happens in the Handler where it waits |
|
| 699 |
-// for 1 or more bytes to be available to then write here. So at this |
|
| 700 |
-// point we know that we have flow control. But this might have to |
|
| 701 |
-// change when priority is implemented, so the serve goroutine knows |
|
| 702 |
-// the total amount of bytes waiting to be sent and can can have more |
|
| 703 |
-// scheduling decisions available. |
|
| 704 |
-func (sc *serverConn) writeDataFromHandler(stream *stream, writeData *writeData, ch chan error) error {
|
|
| 705 |
- sc.writeFrameFromHandler(frameWriteMsg{
|
|
| 706 |
- write: writeData, |
|
| 694 |
+var errChanPool = sync.Pool{
|
|
| 695 |
+ New: func() interface{} { return make(chan error, 1) },
|
|
| 696 |
+} |
|
| 697 |
+ |
|
| 698 |
+var writeDataPool = sync.Pool{
|
|
| 699 |
+ New: func() interface{} { return new(writeData) },
|
|
| 700 |
+} |
|
| 701 |
+ |
|
| 702 |
+// writeDataFromHandler writes DATA response frames from a handler on |
|
| 703 |
+// the given stream. |
|
| 704 |
+func (sc *serverConn) writeDataFromHandler(stream *stream, data []byte, endStream bool) error {
|
|
| 705 |
+ ch := errChanPool.Get().(chan error) |
|
| 706 |
+ writeArg := writeDataPool.Get().(*writeData) |
|
| 707 |
+ *writeArg = writeData{stream.id, data, endStream}
|
|
| 708 |
+ err := sc.writeFrameFromHandler(frameWriteMsg{
|
|
| 709 |
+ write: writeArg, |
|
| 707 | 710 |
stream: stream, |
| 708 | 711 |
done: ch, |
| 709 | 712 |
}) |
| 710 |
- select {
|
|
| 711 |
- case err := <-ch: |
|
| 713 |
+ if err != nil {
|
|
| 712 | 714 |
return err |
| 715 |
+ } |
|
| 716 |
+ var frameWriteDone bool // the frame write is done (successfully or not) |
|
| 717 |
+ select {
|
|
| 718 |
+ case err = <-ch: |
|
| 719 |
+ frameWriteDone = true |
|
| 713 | 720 |
case <-sc.doneServing: |
| 714 | 721 |
return errClientDisconnected |
| 715 | 722 |
case <-stream.cw: |
| 716 |
- return errStreamBroken |
|
| 723 |
+ // If both ch and stream.cw were ready (as might |
|
| 724 |
+ // happen on the final Write after an http.Handler |
|
| 725 |
+ // ends), prefer the write result. Otherwise this |
|
| 726 |
+ // might just be us successfully closing the stream. |
|
| 727 |
+ // The writeFrameAsync and serve goroutines guarantee |
|
| 728 |
+ // that the ch send will happen before the stream.cw |
|
| 729 |
+ // close. |
|
| 730 |
+ select {
|
|
| 731 |
+ case err = <-ch: |
|
| 732 |
+ frameWriteDone = true |
|
| 733 |
+ default: |
|
| 734 |
+ return errStreamClosed |
|
| 735 |
+ } |
|
| 736 |
+ } |
|
| 737 |
+ errChanPool.Put(ch) |
|
| 738 |
+ if frameWriteDone {
|
|
| 739 |
+ writeDataPool.Put(writeArg) |
|
| 717 | 740 |
} |
| 741 |
+ return err |
|
| 718 | 742 |
} |
| 719 | 743 |
|
| 720 | 744 |
// writeFrameFromHandler sends wm to sc.wantWriteFrameCh, but aborts |
| ... | ... |
@@ -724,12 +818,15 @@ func (sc *serverConn) writeDataFromHandler(stream *stream, writeData *writeData, |
| 724 | 724 |
// deadlock writing to sc.wantWriteFrameCh (which is only mildly |
| 725 | 725 |
// buffered and is read by serve itself). If you're on the serve |
| 726 | 726 |
// goroutine, call writeFrame instead. |
| 727 |
-func (sc *serverConn) writeFrameFromHandler(wm frameWriteMsg) {
|
|
| 727 |
+func (sc *serverConn) writeFrameFromHandler(wm frameWriteMsg) error {
|
|
| 728 | 728 |
sc.serveG.checkNotOn() // NOT |
| 729 | 729 |
select {
|
| 730 | 730 |
case sc.wantWriteFrameCh <- wm: |
| 731 |
+ return nil |
|
| 731 | 732 |
case <-sc.doneServing: |
| 733 |
+ // Serve loop is gone. |
|
| 732 | 734 |
// Client has closed their connection to the server. |
| 735 |
+ return errClientDisconnected |
|
| 733 | 736 |
} |
| 734 | 737 |
} |
| 735 | 738 |
|
| ... | ... |
@@ -755,7 +852,6 @@ func (sc *serverConn) startFrameWrite(wm frameWriteMsg) {
|
| 755 | 755 |
if sc.writingFrame {
|
| 756 | 756 |
panic("internal error: can only be writing one frame at a time")
|
| 757 | 757 |
} |
| 758 |
- sc.writingFrame = true |
|
| 759 | 758 |
|
| 760 | 759 |
st := wm.stream |
| 761 | 760 |
if st != nil {
|
| ... | ... |
@@ -764,16 +860,53 @@ func (sc *serverConn) startFrameWrite(wm frameWriteMsg) {
|
| 764 | 764 |
panic("internal error: attempt to send frame on half-closed-local stream")
|
| 765 | 765 |
case stateClosed: |
| 766 | 766 |
if st.sentReset || st.gotReset {
|
| 767 |
- // Skip this frame. But fake the frame write to reschedule: |
|
| 768 |
- sc.wroteFrameCh <- struct{}{}
|
|
| 767 |
+ // Skip this frame. |
|
| 768 |
+ sc.scheduleFrameWrite() |
|
| 769 | 769 |
return |
| 770 | 770 |
} |
| 771 | 771 |
panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wm))
|
| 772 | 772 |
} |
| 773 | 773 |
} |
| 774 | 774 |
|
| 775 |
+ sc.writingFrame = true |
|
| 775 | 776 |
sc.needsFrameFlush = true |
| 776 |
- if endsStream(wm.write) {
|
|
| 777 |
+ go sc.writeFrameAsync(wm) |
|
| 778 |
+} |
|
| 779 |
+ |
|
| 780 |
+// errHandlerPanicked is the error given to any callers blocked in a read from |
|
| 781 |
+// Request.Body when the main goroutine panics. Since most handlers read in the |
|
| 782 |
+// the main ServeHTTP goroutine, this will show up rarely. |
|
| 783 |
+var errHandlerPanicked = errors.New("http2: handler panicked")
|
|
| 784 |
+ |
|
| 785 |
+// wroteFrame is called on the serve goroutine with the result of |
|
| 786 |
+// whatever happened on writeFrameAsync. |
|
| 787 |
+func (sc *serverConn) wroteFrame(res frameWriteResult) {
|
|
| 788 |
+ sc.serveG.check() |
|
| 789 |
+ if !sc.writingFrame {
|
|
| 790 |
+ panic("internal error: expected to be already writing a frame")
|
|
| 791 |
+ } |
|
| 792 |
+ sc.writingFrame = false |
|
| 793 |
+ |
|
| 794 |
+ wm := res.wm |
|
| 795 |
+ st := wm.stream |
|
| 796 |
+ |
|
| 797 |
+ closeStream := endsStream(wm.write) |
|
| 798 |
+ |
|
| 799 |
+ if _, ok := wm.write.(handlerPanicRST); ok {
|
|
| 800 |
+ sc.closeStream(st, errHandlerPanicked) |
|
| 801 |
+ } |
|
| 802 |
+ |
|
| 803 |
+ // Reply (if requested) to the blocked ServeHTTP goroutine. |
|
| 804 |
+ if ch := wm.done; ch != nil {
|
|
| 805 |
+ select {
|
|
| 806 |
+ case ch <- res.err: |
|
| 807 |
+ default: |
|
| 808 |
+ panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wm.write))
|
|
| 809 |
+ } |
|
| 810 |
+ } |
|
| 811 |
+ wm.write = nil // prevent use (assume it's tainted after wm.done send) |
|
| 812 |
+ |
|
| 813 |
+ if closeStream {
|
|
| 777 | 814 |
if st == nil {
|
| 778 | 815 |
panic("internal error: expecting non-nil stream")
|
| 779 | 816 |
} |
| ... | ... |
@@ -791,10 +924,11 @@ func (sc *serverConn) startFrameWrite(wm frameWriteMsg) {
|
| 791 | 791 |
errCancel := StreamError{st.id, ErrCodeCancel}
|
| 792 | 792 |
sc.resetStream(errCancel) |
| 793 | 793 |
case stateHalfClosedRemote: |
| 794 |
- sc.closeStream(st, nil) |
|
| 794 |
+ sc.closeStream(st, errHandlerComplete) |
|
| 795 | 795 |
} |
| 796 | 796 |
} |
| 797 |
- go sc.writeFrameAsync(wm) |
|
| 797 |
+ |
|
| 798 |
+ sc.scheduleFrameWrite() |
|
| 798 | 799 |
} |
| 799 | 800 |
|
| 800 | 801 |
// scheduleFrameWrite tickles the frame writing scheduler. |
| ... | ... |
@@ -874,32 +1008,18 @@ func (sc *serverConn) resetStream(se StreamError) {
|
| 874 | 874 |
} |
| 875 | 875 |
} |
| 876 | 876 |
|
| 877 |
-// curHeaderStreamID returns the stream ID of the header block we're |
|
| 878 |
-// currently in the middle of reading. If this returns non-zero, the |
|
| 879 |
-// next frame must be a CONTINUATION with this stream id. |
|
| 880 |
-func (sc *serverConn) curHeaderStreamID() uint32 {
|
|
| 881 |
- sc.serveG.check() |
|
| 882 |
- st := sc.req.stream |
|
| 883 |
- if st == nil {
|
|
| 884 |
- return 0 |
|
| 885 |
- } |
|
| 886 |
- return st.id |
|
| 887 |
-} |
|
| 888 |
- |
|
| 889 | 877 |
// processFrameFromReader processes the serve loop's read from readFrameCh from the |
| 890 | 878 |
// frame-reading goroutine. |
| 891 | 879 |
// processFrameFromReader returns whether the connection should be kept open. |
| 892 |
-func (sc *serverConn) processFrameFromReader(fg frameAndGate, fgValid bool) bool {
|
|
| 880 |
+func (sc *serverConn) processFrameFromReader(res readFrameResult) bool {
|
|
| 893 | 881 |
sc.serveG.check() |
| 894 |
- var clientGone bool |
|
| 895 |
- var err error |
|
| 896 |
- if !fgValid {
|
|
| 897 |
- err = <-sc.readFrameErrCh |
|
| 882 |
+ err := res.err |
|
| 883 |
+ if err != nil {
|
|
| 898 | 884 |
if err == ErrFrameTooLarge {
|
| 899 | 885 |
sc.goAway(ErrCodeFrameSize) |
| 900 | 886 |
return true // goAway will close the loop |
| 901 | 887 |
} |
| 902 |
- clientGone = err == io.EOF || strings.Contains(err.Error(), "use of closed network connection") |
|
| 888 |
+ clientGone := err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) |
|
| 903 | 889 |
if clientGone {
|
| 904 | 890 |
// TODO: could we also get into this state if |
| 905 | 891 |
// the peer does a half close |
| ... | ... |
@@ -911,13 +1031,12 @@ func (sc *serverConn) processFrameFromReader(fg frameAndGate, fgValid bool) bool |
| 911 | 911 |
// just for testing we could have a non-TLS mode. |
| 912 | 912 |
return false |
| 913 | 913 |
} |
| 914 |
- } |
|
| 915 |
- |
|
| 916 |
- if fgValid {
|
|
| 917 |
- f := fg.f |
|
| 918 |
- sc.vlogf("got %v: %#v", f.Header(), f)
|
|
| 914 |
+ } else {
|
|
| 915 |
+ f := res.f |
|
| 916 |
+ if VerboseLogs {
|
|
| 917 |
+ sc.vlogf("http2: server read frame %v", summarizeFrame(f))
|
|
| 918 |
+ } |
|
| 919 | 919 |
err = sc.processFrame(f) |
| 920 |
- fg.g.Done() // unblock the readFrames goroutine |
|
| 921 | 920 |
if err == nil {
|
| 922 | 921 |
return true |
| 923 | 922 |
} |
| ... | ... |
@@ -931,17 +1050,17 @@ func (sc *serverConn) processFrameFromReader(fg frameAndGate, fgValid bool) bool |
| 931 | 931 |
sc.goAway(ErrCodeFlowControl) |
| 932 | 932 |
return true |
| 933 | 933 |
case ConnectionError: |
| 934 |
- sc.logf("%v: %v", sc.conn.RemoteAddr(), ev)
|
|
| 934 |
+ sc.logf("http2: server connection error from %v: %v", sc.conn.RemoteAddr(), ev)
|
|
| 935 | 935 |
sc.goAway(ErrCode(ev)) |
| 936 | 936 |
return true // goAway will handle shutdown |
| 937 | 937 |
default: |
| 938 |
- if !fgValid {
|
|
| 939 |
- sc.logf("disconnecting; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
|
|
| 938 |
+ if res.err != nil {
|
|
| 939 |
+ sc.vlogf("http2: server closing client connection; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
|
|
| 940 | 940 |
} else {
|
| 941 |
- sc.logf("disconnection due to other error: %v", err)
|
|
| 941 |
+ sc.logf("http2: server closing client connection: %v", err)
|
|
| 942 | 942 |
} |
| 943 |
+ return false |
|
| 943 | 944 |
} |
| 944 |
- return false |
|
| 945 | 945 |
} |
| 946 | 946 |
|
| 947 | 947 |
func (sc *serverConn) processFrame(f Frame) error {
|
| ... | ... |
@@ -955,21 +1074,11 @@ func (sc *serverConn) processFrame(f Frame) error {
|
| 955 | 955 |
sc.sawFirstSettings = true |
| 956 | 956 |
} |
| 957 | 957 |
|
| 958 |
- if s := sc.curHeaderStreamID(); s != 0 {
|
|
| 959 |
- if cf, ok := f.(*ContinuationFrame); !ok {
|
|
| 960 |
- return ConnectionError(ErrCodeProtocol) |
|
| 961 |
- } else if cf.Header().StreamID != s {
|
|
| 962 |
- return ConnectionError(ErrCodeProtocol) |
|
| 963 |
- } |
|
| 964 |
- } |
|
| 965 |
- |
|
| 966 | 958 |
switch f := f.(type) {
|
| 967 | 959 |
case *SettingsFrame: |
| 968 | 960 |
return sc.processSettings(f) |
| 969 |
- case *HeadersFrame: |
|
| 961 |
+ case *MetaHeadersFrame: |
|
| 970 | 962 |
return sc.processHeaders(f) |
| 971 |
- case *ContinuationFrame: |
|
| 972 |
- return sc.processContinuation(f) |
|
| 973 | 963 |
case *WindowUpdateFrame: |
| 974 | 964 |
return sc.processWindowUpdate(f) |
| 975 | 965 |
case *PingFrame: |
| ... | ... |
@@ -985,14 +1094,14 @@ func (sc *serverConn) processFrame(f Frame) error {
|
| 985 | 985 |
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. |
| 986 | 986 |
return ConnectionError(ErrCodeProtocol) |
| 987 | 987 |
default: |
| 988 |
- log.Printf("Ignoring frame: %v", f.Header())
|
|
| 988 |
+ sc.vlogf("http2: server ignoring frame: %v", f.Header())
|
|
| 989 | 989 |
return nil |
| 990 | 990 |
} |
| 991 | 991 |
} |
| 992 | 992 |
|
| 993 | 993 |
func (sc *serverConn) processPing(f *PingFrame) error {
|
| 994 | 994 |
sc.serveG.check() |
| 995 |
- if f.Flags.Has(FlagSettingsAck) {
|
|
| 995 |
+ if f.IsAck() {
|
|
| 996 | 996 |
// 6.7 PING: " An endpoint MUST NOT respond to PING frames |
| 997 | 997 |
// containing this flag." |
| 998 | 998 |
return nil |
| ... | ... |
@@ -1060,12 +1169,27 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
| 1060 | 1060 |
} |
| 1061 | 1061 |
st.state = stateClosed |
| 1062 | 1062 |
sc.curOpenStreams-- |
| 1063 |
+ if sc.curOpenStreams == 0 {
|
|
| 1064 |
+ sc.setConnState(http.StateIdle) |
|
| 1065 |
+ } |
|
| 1063 | 1066 |
delete(sc.streams, st.id) |
| 1064 | 1067 |
if p := st.body; p != nil {
|
| 1065 |
- p.Close(err) |
|
| 1068 |
+ p.CloseWithError(err) |
|
| 1066 | 1069 |
} |
| 1067 | 1070 |
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc |
| 1068 | 1071 |
sc.writeSched.forgetStream(st.id) |
| 1072 |
+ if st.reqBuf != nil {
|
|
| 1073 |
+ // Stash this request body buffer (64k) away for reuse |
|
| 1074 |
+ // by a future POST/PUT/etc. |
|
| 1075 |
+ // |
|
| 1076 |
+ // TODO(bradfitz): share on the server? sync.Pool? |
|
| 1077 |
+ // Server requires locks and might hurt contention. |
|
| 1078 |
+ // sync.Pool might work, or might be worse, depending |
|
| 1079 |
+ // on goroutine CPU migrations. (get and put on |
|
| 1080 |
+ // separate CPUs). Maybe a mix of strategies. But |
|
| 1081 |
+ // this is an easy win for now. |
|
| 1082 |
+ sc.freeRequestBodyBuf = st.reqBuf |
|
| 1083 |
+ } |
|
| 1069 | 1084 |
} |
| 1070 | 1085 |
|
| 1071 | 1086 |
func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
| ... | ... |
@@ -1093,7 +1217,9 @@ func (sc *serverConn) processSetting(s Setting) error {
|
| 1093 | 1093 |
if err := s.Valid(); err != nil {
|
| 1094 | 1094 |
return err |
| 1095 | 1095 |
} |
| 1096 |
- sc.vlogf("processing setting %v", s)
|
|
| 1096 |
+ if VerboseLogs {
|
|
| 1097 |
+ sc.vlogf("http2: server processing setting %v", s)
|
|
| 1098 |
+ } |
|
| 1097 | 1099 |
switch s.ID {
|
| 1098 | 1100 |
case SettingHeaderTableSize: |
| 1099 | 1101 |
sc.headerTableSize = s.Val |
| ... | ... |
@@ -1107,11 +1233,14 @@ func (sc *serverConn) processSetting(s Setting) error {
|
| 1107 | 1107 |
case SettingMaxFrameSize: |
| 1108 | 1108 |
sc.writeSched.maxFrameSize = s.Val |
| 1109 | 1109 |
case SettingMaxHeaderListSize: |
| 1110 |
- sc.maxHeaderListSize = s.Val |
|
| 1110 |
+ sc.peerMaxHeaderListSize = s.Val |
|
| 1111 | 1111 |
default: |
| 1112 | 1112 |
// Unknown setting: "An endpoint that receives a SETTINGS |
| 1113 | 1113 |
// frame with any unknown or unsupported identifier MUST |
| 1114 | 1114 |
// ignore that setting." |
| 1115 |
+ if VerboseLogs {
|
|
| 1116 |
+ sc.vlogf("http2: server ignoring unknown setting %v", s)
|
|
| 1117 |
+ } |
|
| 1115 | 1118 |
} |
| 1116 | 1119 |
return nil |
| 1117 | 1120 |
} |
| ... | ... |
@@ -1151,7 +1280,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
| 1151 | 1151 |
// with a stream error (Section 5.4.2) of type STREAM_CLOSED." |
| 1152 | 1152 |
id := f.Header().StreamID |
| 1153 | 1153 |
st, ok := sc.streams[id] |
| 1154 |
- if !ok || st.state != stateOpen {
|
|
| 1154 |
+ if !ok || st.state != stateOpen || st.gotTrailerHeader {
|
|
| 1155 | 1155 |
// This includes sending a RST_STREAM if the stream is |
| 1156 | 1156 |
// in stateHalfClosedLocal (which currently means that |
| 1157 | 1157 |
// the http.Handler returned, so it's done reading & |
| ... | ... |
@@ -1166,7 +1295,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
| 1166 | 1166 |
|
| 1167 | 1167 |
// Sender sending more than they'd declared? |
| 1168 | 1168 |
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
| 1169 |
- st.body.Close(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
|
| 1169 |
+ st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
|
| 1170 | 1170 |
return StreamError{id, ErrCodeStreamClosed}
|
| 1171 | 1171 |
} |
| 1172 | 1172 |
if len(data) > 0 {
|
| ... | ... |
@@ -1185,18 +1314,39 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
| 1185 | 1185 |
st.bodyBytes += int64(len(data)) |
| 1186 | 1186 |
} |
| 1187 | 1187 |
if f.StreamEnded() {
|
| 1188 |
- if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
|
|
| 1189 |
- st.body.Close(fmt.Errorf("request declared a Content-Length of %d but only wrote %d bytes",
|
|
| 1190 |
- st.declBodyBytes, st.bodyBytes)) |
|
| 1191 |
- } else {
|
|
| 1192 |
- st.body.Close(io.EOF) |
|
| 1193 |
- } |
|
| 1194 |
- st.state = stateHalfClosedRemote |
|
| 1188 |
+ st.endStream() |
|
| 1195 | 1189 |
} |
| 1196 | 1190 |
return nil |
| 1197 | 1191 |
} |
| 1198 | 1192 |
|
| 1199 |
-func (sc *serverConn) processHeaders(f *HeadersFrame) error {
|
|
| 1193 |
+// endStream closes a Request.Body's pipe. It is called when a DATA |
|
| 1194 |
+// frame says a request body is over (or after trailers). |
|
| 1195 |
+func (st *stream) endStream() {
|
|
| 1196 |
+ sc := st.sc |
|
| 1197 |
+ sc.serveG.check() |
|
| 1198 |
+ |
|
| 1199 |
+ if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
|
|
| 1200 |
+ st.body.CloseWithError(fmt.Errorf("request declared a Content-Length of %d but only wrote %d bytes",
|
|
| 1201 |
+ st.declBodyBytes, st.bodyBytes)) |
|
| 1202 |
+ } else {
|
|
| 1203 |
+ st.body.closeWithErrorAndCode(io.EOF, st.copyTrailersToHandlerRequest) |
|
| 1204 |
+ st.body.CloseWithError(io.EOF) |
|
| 1205 |
+ } |
|
| 1206 |
+ st.state = stateHalfClosedRemote |
|
| 1207 |
+} |
|
| 1208 |
+ |
|
| 1209 |
+// copyTrailersToHandlerRequest is run in the Handler's goroutine in |
|
| 1210 |
+// its Request.Body.Read just before it gets io.EOF. |
|
| 1211 |
+func (st *stream) copyTrailersToHandlerRequest() {
|
|
| 1212 |
+ for k, vv := range st.trailer {
|
|
| 1213 |
+ if _, ok := st.reqTrailer[k]; ok {
|
|
| 1214 |
+ // Only copy it over it was pre-declared. |
|
| 1215 |
+ st.reqTrailer[k] = vv |
|
| 1216 |
+ } |
|
| 1217 |
+ } |
|
| 1218 |
+} |
|
| 1219 |
+ |
|
| 1220 |
+func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|
| 1200 | 1221 |
sc.serveG.check() |
| 1201 | 1222 |
id := f.Header().StreamID |
| 1202 | 1223 |
if sc.inGoAway {
|
| ... | ... |
@@ -1204,20 +1354,34 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
|
| 1204 | 1204 |
return nil |
| 1205 | 1205 |
} |
| 1206 | 1206 |
// http://http2.github.io/http2-spec/#rfc.section.5.1.1 |
| 1207 |
- if id%2 != 1 || id <= sc.maxStreamID || sc.req.stream != nil {
|
|
| 1208 |
- // Streams initiated by a client MUST use odd-numbered |
|
| 1209 |
- // stream identifiers. [...] The identifier of a newly |
|
| 1210 |
- // established stream MUST be numerically greater than all |
|
| 1211 |
- // streams that the initiating endpoint has opened or |
|
| 1212 |
- // reserved. [...] An endpoint that receives an unexpected |
|
| 1213 |
- // stream identifier MUST respond with a connection error |
|
| 1214 |
- // (Section 5.4.1) of type PROTOCOL_ERROR. |
|
| 1207 |
+ // Streams initiated by a client MUST use odd-numbered stream |
|
| 1208 |
+ // identifiers. [...] An endpoint that receives an unexpected |
|
| 1209 |
+ // stream identifier MUST respond with a connection error |
|
| 1210 |
+ // (Section 5.4.1) of type PROTOCOL_ERROR. |
|
| 1211 |
+ if id%2 != 1 {
|
|
| 1215 | 1212 |
return ConnectionError(ErrCodeProtocol) |
| 1216 | 1213 |
} |
| 1217 |
- if id > sc.maxStreamID {
|
|
| 1218 |
- sc.maxStreamID = id |
|
| 1214 |
+ // A HEADERS frame can be used to create a new stream or |
|
| 1215 |
+ // send a trailer for an open one. If we already have a stream |
|
| 1216 |
+ // open, let it process its own HEADERS frame (trailers at this |
|
| 1217 |
+ // point, if it's valid). |
|
| 1218 |
+ st := sc.streams[f.Header().StreamID] |
|
| 1219 |
+ if st != nil {
|
|
| 1220 |
+ return st.processTrailerHeaders(f) |
|
| 1219 | 1221 |
} |
| 1220 |
- st := &stream{
|
|
| 1222 |
+ |
|
| 1223 |
+ // [...] The identifier of a newly established stream MUST be |
|
| 1224 |
+ // numerically greater than all streams that the initiating |
|
| 1225 |
+ // endpoint has opened or reserved. [...] An endpoint that |
|
| 1226 |
+ // receives an unexpected stream identifier MUST respond with |
|
| 1227 |
+ // a connection error (Section 5.4.1) of type PROTOCOL_ERROR. |
|
| 1228 |
+ if id <= sc.maxStreamID {
|
|
| 1229 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 1230 |
+ } |
|
| 1231 |
+ sc.maxStreamID = id |
|
| 1232 |
+ |
|
| 1233 |
+ st = &stream{
|
|
| 1234 |
+ sc: sc, |
|
| 1221 | 1235 |
id: id, |
| 1222 | 1236 |
state: stateOpen, |
| 1223 | 1237 |
} |
| ... | ... |
@@ -1236,36 +1400,9 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
|
| 1236 | 1236 |
adjustStreamPriority(sc.streams, st.id, f.Priority) |
| 1237 | 1237 |
} |
| 1238 | 1238 |
sc.curOpenStreams++ |
| 1239 |
- sc.req = requestParam{
|
|
| 1240 |
- stream: st, |
|
| 1241 |
- header: make(http.Header), |
|
| 1242 |
- } |
|
| 1243 |
- return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded()) |
|
| 1244 |
-} |
|
| 1245 |
- |
|
| 1246 |
-func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
|
|
| 1247 |
- sc.serveG.check() |
|
| 1248 |
- st := sc.streams[f.Header().StreamID] |
|
| 1249 |
- if st == nil || sc.curHeaderStreamID() != st.id {
|
|
| 1250 |
- return ConnectionError(ErrCodeProtocol) |
|
| 1251 |
- } |
|
| 1252 |
- return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded()) |
|
| 1253 |
-} |
|
| 1254 |
- |
|
| 1255 |
-func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
|
|
| 1256 |
- sc.serveG.check() |
|
| 1257 |
- if _, err := sc.hpackDecoder.Write(frag); err != nil {
|
|
| 1258 |
- // TODO: convert to stream error I assume? |
|
| 1259 |
- return err |
|
| 1239 |
+ if sc.curOpenStreams == 1 {
|
|
| 1240 |
+ sc.setConnState(http.StateActive) |
|
| 1260 | 1241 |
} |
| 1261 |
- if !end {
|
|
| 1262 |
- return nil |
|
| 1263 |
- } |
|
| 1264 |
- if err := sc.hpackDecoder.Close(); err != nil {
|
|
| 1265 |
- // TODO: convert to stream error I assume? |
|
| 1266 |
- return err |
|
| 1267 |
- } |
|
| 1268 |
- defer sc.resetPendingRequest() |
|
| 1269 | 1242 |
if sc.curOpenStreams > sc.advMaxStreams {
|
| 1270 | 1243 |
// "Endpoints MUST NOT exceed the limit set by their |
| 1271 | 1244 |
// peer. An endpoint that receives a HEADERS frame |
| ... | ... |
@@ -1285,13 +1422,48 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo |
| 1285 | 1285 |
return StreamError{st.id, ErrCodeRefusedStream}
|
| 1286 | 1286 |
} |
| 1287 | 1287 |
|
| 1288 |
- rw, req, err := sc.newWriterAndRequest() |
|
| 1288 |
+ rw, req, err := sc.newWriterAndRequest(st, f) |
|
| 1289 | 1289 |
if err != nil {
|
| 1290 | 1290 |
return err |
| 1291 | 1291 |
} |
| 1292 |
+ st.reqTrailer = req.Trailer |
|
| 1293 |
+ if st.reqTrailer != nil {
|
|
| 1294 |
+ st.trailer = make(http.Header) |
|
| 1295 |
+ } |
|
| 1292 | 1296 |
st.body = req.Body.(*requestBody).pipe // may be nil |
| 1293 | 1297 |
st.declBodyBytes = req.ContentLength |
| 1294 |
- go sc.runHandler(rw, req) |
|
| 1298 |
+ |
|
| 1299 |
+ handler := sc.handler.ServeHTTP |
|
| 1300 |
+ if f.Truncated {
|
|
| 1301 |
+ // Their header list was too long. Send a 431 error. |
|
| 1302 |
+ handler = handleHeaderListTooLong |
|
| 1303 |
+ } |
|
| 1304 |
+ |
|
| 1305 |
+ go sc.runHandler(rw, req, handler) |
|
| 1306 |
+ return nil |
|
| 1307 |
+} |
|
| 1308 |
+ |
|
| 1309 |
+func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|
| 1310 |
+ sc := st.sc |
|
| 1311 |
+ sc.serveG.check() |
|
| 1312 |
+ if st.gotTrailerHeader {
|
|
| 1313 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 1314 |
+ } |
|
| 1315 |
+ st.gotTrailerHeader = true |
|
| 1316 |
+ if !f.StreamEnded() {
|
|
| 1317 |
+ return StreamError{st.id, ErrCodeProtocol}
|
|
| 1318 |
+ } |
|
| 1319 |
+ |
|
| 1320 |
+ if len(f.PseudoFields()) > 0 {
|
|
| 1321 |
+ return StreamError{st.id, ErrCodeProtocol}
|
|
| 1322 |
+ } |
|
| 1323 |
+ if st.trailer != nil {
|
|
| 1324 |
+ for _, hf := range f.RegularFields() {
|
|
| 1325 |
+ key := sc.canonicalHeader(hf.Name) |
|
| 1326 |
+ st.trailer[key] = append(st.trailer[key], hf.Value) |
|
| 1327 |
+ } |
|
| 1328 |
+ } |
|
| 1329 |
+ st.endStream() |
|
| 1295 | 1330 |
return nil |
| 1296 | 1331 |
} |
| 1297 | 1332 |
|
| ... | ... |
@@ -1336,19 +1508,21 @@ func adjustStreamPriority(streams map[uint32]*stream, streamID uint32, priority |
| 1336 | 1336 |
} |
| 1337 | 1337 |
} |
| 1338 | 1338 |
|
| 1339 |
-// resetPendingRequest zeros out all state related to a HEADERS frame |
|
| 1340 |
-// and its zero or more CONTINUATION frames sent to start a new |
|
| 1341 |
-// request. |
|
| 1342 |
-func (sc *serverConn) resetPendingRequest() {
|
|
| 1339 |
+func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*responseWriter, *http.Request, error) {
|
|
| 1343 | 1340 |
sc.serveG.check() |
| 1344 |
- sc.req = requestParam{}
|
|
| 1345 |
-} |
|
| 1346 | 1341 |
|
| 1347 |
-func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
|
|
| 1348 |
- sc.serveG.check() |
|
| 1349 |
- rp := &sc.req |
|
| 1350 |
- if rp.invalidHeader || rp.method == "" || rp.path == "" || |
|
| 1351 |
- (rp.scheme != "https" && rp.scheme != "http") {
|
|
| 1342 |
+ method := f.PseudoValue("method")
|
|
| 1343 |
+ path := f.PseudoValue("path")
|
|
| 1344 |
+ scheme := f.PseudoValue("scheme")
|
|
| 1345 |
+ authority := f.PseudoValue("authority")
|
|
| 1346 |
+ |
|
| 1347 |
+ isConnect := method == "CONNECT" |
|
| 1348 |
+ if isConnect {
|
|
| 1349 |
+ if path != "" || scheme != "" || authority == "" {
|
|
| 1350 |
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
|
| 1351 |
+ } |
|
| 1352 |
+ } else if method == "" || path == "" || |
|
| 1353 |
+ (scheme != "https" && scheme != "http") {
|
|
| 1352 | 1354 |
// See 8.1.2.6 Malformed Requests and Responses: |
| 1353 | 1355 |
// |
| 1354 | 1356 |
// Malformed requests or responses that are detected |
| ... | ... |
@@ -1359,52 +1533,99 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err |
| 1359 | 1359 |
// "All HTTP/2 requests MUST include exactly one valid |
| 1360 | 1360 |
// value for the :method, :scheme, and :path |
| 1361 | 1361 |
// pseudo-header fields" |
| 1362 |
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
|
| 1362 |
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
|
| 1363 |
+ } |
|
| 1364 |
+ |
|
| 1365 |
+ bodyOpen := !f.StreamEnded() |
|
| 1366 |
+ if method == "HEAD" && bodyOpen {
|
|
| 1367 |
+ // HEAD requests can't have bodies |
|
| 1368 |
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
|
| 1363 | 1369 |
} |
| 1364 | 1370 |
var tlsState *tls.ConnectionState // nil if not scheme https |
| 1365 |
- if rp.scheme == "https" {
|
|
| 1371 |
+ |
|
| 1372 |
+ if scheme == "https" {
|
|
| 1366 | 1373 |
tlsState = sc.tlsState |
| 1367 | 1374 |
} |
| 1368 |
- authority := rp.authority |
|
| 1375 |
+ |
|
| 1376 |
+ header := make(http.Header) |
|
| 1377 |
+ for _, hf := range f.RegularFields() {
|
|
| 1378 |
+ header.Add(sc.canonicalHeader(hf.Name), hf.Value) |
|
| 1379 |
+ } |
|
| 1380 |
+ |
|
| 1369 | 1381 |
if authority == "" {
|
| 1370 |
- authority = rp.header.Get("Host")
|
|
| 1382 |
+ authority = header.Get("Host")
|
|
| 1371 | 1383 |
} |
| 1372 |
- needsContinue := rp.header.Get("Expect") == "100-continue"
|
|
| 1384 |
+ needsContinue := header.Get("Expect") == "100-continue"
|
|
| 1373 | 1385 |
if needsContinue {
|
| 1374 |
- rp.header.Del("Expect")
|
|
| 1386 |
+ header.Del("Expect")
|
|
| 1387 |
+ } |
|
| 1388 |
+ // Merge Cookie headers into one "; "-delimited value. |
|
| 1389 |
+ if cookies := header["Cookie"]; len(cookies) > 1 {
|
|
| 1390 |
+ header.Set("Cookie", strings.Join(cookies, "; "))
|
|
| 1391 |
+ } |
|
| 1392 |
+ |
|
| 1393 |
+ // Setup Trailers |
|
| 1394 |
+ var trailer http.Header |
|
| 1395 |
+ for _, v := range header["Trailer"] {
|
|
| 1396 |
+ for _, key := range strings.Split(v, ",") {
|
|
| 1397 |
+ key = http.CanonicalHeaderKey(strings.TrimSpace(key)) |
|
| 1398 |
+ switch key {
|
|
| 1399 |
+ case "Transfer-Encoding", "Trailer", "Content-Length": |
|
| 1400 |
+ // Bogus. (copy of http1 rules) |
|
| 1401 |
+ // Ignore. |
|
| 1402 |
+ default: |
|
| 1403 |
+ if trailer == nil {
|
|
| 1404 |
+ trailer = make(http.Header) |
|
| 1405 |
+ } |
|
| 1406 |
+ trailer[key] = nil |
|
| 1407 |
+ } |
|
| 1408 |
+ } |
|
| 1375 | 1409 |
} |
| 1376 |
- bodyOpen := rp.stream.state == stateOpen |
|
| 1410 |
+ delete(header, "Trailer") |
|
| 1411 |
+ |
|
| 1377 | 1412 |
body := &requestBody{
|
| 1378 | 1413 |
conn: sc, |
| 1379 |
- stream: rp.stream, |
|
| 1414 |
+ stream: st, |
|
| 1380 | 1415 |
needsContinue: needsContinue, |
| 1381 | 1416 |
} |
| 1382 |
- // TODO: handle asterisk '*' requests + test |
|
| 1383 |
- url, err := url.ParseRequestURI(rp.path) |
|
| 1384 |
- if err != nil {
|
|
| 1385 |
- // TODO: find the right error code? |
|
| 1386 |
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
|
| 1417 |
+ var url_ *url.URL |
|
| 1418 |
+ var requestURI string |
|
| 1419 |
+ if isConnect {
|
|
| 1420 |
+ url_ = &url.URL{Host: authority}
|
|
| 1421 |
+ requestURI = authority // mimic HTTP/1 server behavior |
|
| 1422 |
+ } else {
|
|
| 1423 |
+ var err error |
|
| 1424 |
+ url_, err = url.ParseRequestURI(path) |
|
| 1425 |
+ if err != nil {
|
|
| 1426 |
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
|
| 1427 |
+ } |
|
| 1428 |
+ requestURI = path |
|
| 1387 | 1429 |
} |
| 1388 | 1430 |
req := &http.Request{
|
| 1389 |
- Method: rp.method, |
|
| 1390 |
- URL: url, |
|
| 1431 |
+ Method: method, |
|
| 1432 |
+ URL: url_, |
|
| 1391 | 1433 |
RemoteAddr: sc.remoteAddrStr, |
| 1392 |
- Header: rp.header, |
|
| 1393 |
- RequestURI: rp.path, |
|
| 1434 |
+ Header: header, |
|
| 1435 |
+ RequestURI: requestURI, |
|
| 1394 | 1436 |
Proto: "HTTP/2.0", |
| 1395 | 1437 |
ProtoMajor: 2, |
| 1396 | 1438 |
ProtoMinor: 0, |
| 1397 | 1439 |
TLS: tlsState, |
| 1398 | 1440 |
Host: authority, |
| 1399 | 1441 |
Body: body, |
| 1442 |
+ Trailer: trailer, |
|
| 1400 | 1443 |
} |
| 1401 | 1444 |
if bodyOpen {
|
| 1445 |
+ // Disabled, per golang.org/issue/14960: |
|
| 1446 |
+ // st.reqBuf = sc.getRequestBodyBuf() |
|
| 1447 |
+ // TODO: remove this 64k of garbage per request (again, but without a data race): |
|
| 1448 |
+ buf := make([]byte, initialWindowSize) |
|
| 1449 |
+ |
|
| 1402 | 1450 |
body.pipe = &pipe{
|
| 1403 |
- b: buffer{buf: make([]byte, initialWindowSize)}, // TODO: share/remove XXX
|
|
| 1451 |
+ b: &fixedBuffer{buf: buf},
|
|
| 1404 | 1452 |
} |
| 1405 |
- body.pipe.c.L = &body.pipe.m |
|
| 1406 | 1453 |
|
| 1407 |
- if vv, ok := rp.header["Content-Length"]; ok {
|
|
| 1454 |
+ if vv, ok := header["Content-Length"]; ok {
|
|
| 1408 | 1455 |
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64) |
| 1409 | 1456 |
} else {
|
| 1410 | 1457 |
req.ContentLength = -1 |
| ... | ... |
@@ -1417,25 +1638,59 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err |
| 1417 | 1417 |
rws.conn = sc |
| 1418 | 1418 |
rws.bw = bwSave |
| 1419 | 1419 |
rws.bw.Reset(chunkWriter{rws})
|
| 1420 |
- rws.stream = rp.stream |
|
| 1420 |
+ rws.stream = st |
|
| 1421 | 1421 |
rws.req = req |
| 1422 | 1422 |
rws.body = body |
| 1423 |
- rws.frameWriteCh = make(chan error, 1) |
|
| 1424 | 1423 |
|
| 1425 | 1424 |
rw := &responseWriter{rws: rws}
|
| 1426 | 1425 |
return rw, req, nil |
| 1427 | 1426 |
} |
| 1428 | 1427 |
|
| 1428 |
+func (sc *serverConn) getRequestBodyBuf() []byte {
|
|
| 1429 |
+ sc.serveG.check() |
|
| 1430 |
+ if buf := sc.freeRequestBodyBuf; buf != nil {
|
|
| 1431 |
+ sc.freeRequestBodyBuf = nil |
|
| 1432 |
+ return buf |
|
| 1433 |
+ } |
|
| 1434 |
+ return make([]byte, initialWindowSize) |
|
| 1435 |
+} |
|
| 1436 |
+ |
|
| 1429 | 1437 |
// Run on its own goroutine. |
| 1430 |
-func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request) {
|
|
| 1431 |
- defer rw.handlerDone() |
|
| 1432 |
- // TODO: catch panics like net/http.Server |
|
| 1433 |
- sc.handler.ServeHTTP(rw, req) |
|
| 1438 |
+func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
|
|
| 1439 |
+ didPanic := true |
|
| 1440 |
+ defer func() {
|
|
| 1441 |
+ if didPanic {
|
|
| 1442 |
+ e := recover() |
|
| 1443 |
+ // Same as net/http: |
|
| 1444 |
+ const size = 64 << 10 |
|
| 1445 |
+ buf := make([]byte, size) |
|
| 1446 |
+ buf = buf[:runtime.Stack(buf, false)] |
|
| 1447 |
+ sc.writeFrameFromHandler(frameWriteMsg{
|
|
| 1448 |
+ write: handlerPanicRST{rw.rws.stream.id},
|
|
| 1449 |
+ stream: rw.rws.stream, |
|
| 1450 |
+ }) |
|
| 1451 |
+ sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
|
|
| 1452 |
+ return |
|
| 1453 |
+ } |
|
| 1454 |
+ rw.handlerDone() |
|
| 1455 |
+ }() |
|
| 1456 |
+ handler(rw, req) |
|
| 1457 |
+ didPanic = false |
|
| 1458 |
+} |
|
| 1459 |
+ |
|
| 1460 |
+func handleHeaderListTooLong(w http.ResponseWriter, r *http.Request) {
|
|
| 1461 |
+ // 10.5.1 Limits on Header Block Size: |
|
| 1462 |
+ // .. "A server that receives a larger header block than it is |
|
| 1463 |
+ // willing to handle can send an HTTP 431 (Request Header Fields Too |
|
| 1464 |
+ // Large) status code" |
|
| 1465 |
+ const statusRequestHeaderFieldsTooLarge = 431 // only in Go 1.6+ |
|
| 1466 |
+ w.WriteHeader(statusRequestHeaderFieldsTooLarge) |
|
| 1467 |
+ io.WriteString(w, "<h1>HTTP Error 431</h1><p>Request Header Field(s) Too Large</p>") |
|
| 1434 | 1468 |
} |
| 1435 | 1469 |
|
| 1436 | 1470 |
// called from handler goroutines. |
| 1437 | 1471 |
// h may be nil. |
| 1438 |
-func (sc *serverConn) writeHeaders(st *stream, headerData *writeResHeaders, tempCh chan error) {
|
|
| 1472 |
+func (sc *serverConn) writeHeaders(st *stream, headerData *writeResHeaders) error {
|
|
| 1439 | 1473 |
sc.serveG.checkNotOn() // NOT on |
| 1440 | 1474 |
var errc chan error |
| 1441 | 1475 |
if headerData.h != nil {
|
| ... | ... |
@@ -1443,22 +1698,27 @@ func (sc *serverConn) writeHeaders(st *stream, headerData *writeResHeaders, temp |
| 1443 | 1443 |
// waiting for this frame to be written, so an http.Flush mid-handler |
| 1444 | 1444 |
// writes out the correct value of keys, before a handler later potentially |
| 1445 | 1445 |
// mutates it. |
| 1446 |
- errc = tempCh |
|
| 1446 |
+ errc = errChanPool.Get().(chan error) |
|
| 1447 | 1447 |
} |
| 1448 |
- sc.writeFrameFromHandler(frameWriteMsg{
|
|
| 1448 |
+ if err := sc.writeFrameFromHandler(frameWriteMsg{
|
|
| 1449 | 1449 |
write: headerData, |
| 1450 | 1450 |
stream: st, |
| 1451 | 1451 |
done: errc, |
| 1452 |
- }) |
|
| 1452 |
+ }); err != nil {
|
|
| 1453 |
+ return err |
|
| 1454 |
+ } |
|
| 1453 | 1455 |
if errc != nil {
|
| 1454 | 1456 |
select {
|
| 1455 |
- case <-errc: |
|
| 1456 |
- // Ignore. Just for synchronization. |
|
| 1457 |
- // Any error will be handled in the writing goroutine. |
|
| 1457 |
+ case err := <-errc: |
|
| 1458 |
+ errChanPool.Put(errc) |
|
| 1459 |
+ return err |
|
| 1458 | 1460 |
case <-sc.doneServing: |
| 1459 |
- // Client has closed the connection. |
|
| 1461 |
+ return errClientDisconnected |
|
| 1462 |
+ case <-st.cw: |
|
| 1463 |
+ return errStreamClosed |
|
| 1460 | 1464 |
} |
| 1461 | 1465 |
} |
| 1466 |
+ return nil |
|
| 1462 | 1467 |
} |
| 1463 | 1468 |
|
| 1464 | 1469 |
// called from handler goroutines. |
| ... | ... |
@@ -1481,7 +1741,10 @@ type bodyReadMsg struct {
|
| 1481 | 1481 |
// and schedules flow control tokens to be sent. |
| 1482 | 1482 |
func (sc *serverConn) noteBodyReadFromHandler(st *stream, n int) {
|
| 1483 | 1483 |
sc.serveG.checkNotOn() // NOT on |
| 1484 |
- sc.bodyReadCh <- bodyReadMsg{st, n}
|
|
| 1484 |
+ select {
|
|
| 1485 |
+ case sc.bodyReadCh <- bodyReadMsg{st, n}:
|
|
| 1486 |
+ case <-sc.doneServing: |
|
| 1487 |
+ } |
|
| 1485 | 1488 |
} |
| 1486 | 1489 |
|
| 1487 | 1490 |
func (sc *serverConn) noteBodyRead(st *stream, n int) {
|
| ... | ... |
@@ -1548,7 +1811,7 @@ type requestBody struct {
|
| 1548 | 1548 |
|
| 1549 | 1549 |
func (b *requestBody) Close() error {
|
| 1550 | 1550 |
if b.pipe != nil {
|
| 1551 |
- b.pipe.Close(errClosedBody) |
|
| 1551 |
+ b.pipe.CloseWithError(errClosedBody) |
|
| 1552 | 1552 |
} |
| 1553 | 1553 |
b.closed = true |
| 1554 | 1554 |
return nil |
| ... | ... |
@@ -1599,12 +1862,14 @@ type responseWriterState struct {
|
| 1599 | 1599 |
// mutated by http.Handler goroutine: |
| 1600 | 1600 |
handlerHeader http.Header // nil until called |
| 1601 | 1601 |
snapHeader http.Header // snapshot of handlerHeader at WriteHeader time |
| 1602 |
+ trailers []string // set in writeChunk |
|
| 1602 | 1603 |
status int // status code passed to WriteHeader |
| 1603 | 1604 |
wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. |
| 1604 | 1605 |
sentHeader bool // have we sent the header frame? |
| 1605 | 1606 |
handlerDone bool // handler has finished |
| 1606 |
- curWrite writeData |
|
| 1607 |
- frameWriteCh chan error // re-used whenever we need to block on a frame being written |
|
| 1607 |
+ |
|
| 1608 |
+ sentContentLen int64 // non-zero if handler set a Content-Length header |
|
| 1609 |
+ wroteBytes int64 |
|
| 1608 | 1610 |
|
| 1609 | 1611 |
closeNotifierMu sync.Mutex // guards closeNotifierCh |
| 1610 | 1612 |
closeNotifierCh chan bool // nil until first used |
| ... | ... |
@@ -1614,6 +1879,23 @@ type chunkWriter struct{ rws *responseWriterState }
|
| 1614 | 1614 |
|
| 1615 | 1615 |
func (cw chunkWriter) Write(p []byte) (n int, err error) { return cw.rws.writeChunk(p) }
|
| 1616 | 1616 |
|
| 1617 |
+func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) != 0 }
|
|
| 1618 |
+ |
|
| 1619 |
+// declareTrailer is called for each Trailer header when the |
|
| 1620 |
+// response header is written. It notes that a header will need to be |
|
| 1621 |
+// written in the trailers at the end of the response. |
|
| 1622 |
+func (rws *responseWriterState) declareTrailer(k string) {
|
|
| 1623 |
+ k = http.CanonicalHeaderKey(k) |
|
| 1624 |
+ switch k {
|
|
| 1625 |
+ case "Transfer-Encoding", "Content-Length", "Trailer": |
|
| 1626 |
+ // Forbidden by RFC 2616 14.40. |
|
| 1627 |
+ return |
|
| 1628 |
+ } |
|
| 1629 |
+ if !strSliceContains(rws.trailers, k) {
|
|
| 1630 |
+ rws.trailers = append(rws.trailers, k) |
|
| 1631 |
+ } |
|
| 1632 |
+} |
|
| 1633 |
+ |
|
| 1617 | 1634 |
// writeChunk writes chunks from the bufio.Writer. But because |
| 1618 | 1635 |
// bufio.Writer may bypass its chunking, sometimes p may be |
| 1619 | 1636 |
// arbitrarily large. |
| ... | ... |
@@ -1624,41 +1906,137 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
| 1624 | 1624 |
if !rws.wroteHeader {
|
| 1625 | 1625 |
rws.writeHeader(200) |
| 1626 | 1626 |
} |
| 1627 |
+ |
|
| 1628 |
+ isHeadResp := rws.req.Method == "HEAD" |
|
| 1627 | 1629 |
if !rws.sentHeader {
|
| 1628 | 1630 |
rws.sentHeader = true |
| 1629 |
- var ctype, clen string // implicit ones, if we can calculate it |
|
| 1630 |
- if rws.handlerDone && rws.snapHeader.Get("Content-Length") == "" {
|
|
| 1631 |
+ var ctype, clen string |
|
| 1632 |
+ if clen = rws.snapHeader.Get("Content-Length"); clen != "" {
|
|
| 1633 |
+ rws.snapHeader.Del("Content-Length")
|
|
| 1634 |
+ clen64, err := strconv.ParseInt(clen, 10, 64) |
|
| 1635 |
+ if err == nil && clen64 >= 0 {
|
|
| 1636 |
+ rws.sentContentLen = clen64 |
|
| 1637 |
+ } else {
|
|
| 1638 |
+ clen = "" |
|
| 1639 |
+ } |
|
| 1640 |
+ } |
|
| 1641 |
+ if clen == "" && rws.handlerDone && bodyAllowedForStatus(rws.status) && (len(p) > 0 || !isHeadResp) {
|
|
| 1631 | 1642 |
clen = strconv.Itoa(len(p)) |
| 1632 | 1643 |
} |
| 1633 |
- if rws.snapHeader.Get("Content-Type") == "" {
|
|
| 1644 |
+ _, hasContentType := rws.snapHeader["Content-Type"] |
|
| 1645 |
+ if !hasContentType && bodyAllowedForStatus(rws.status) {
|
|
| 1634 | 1646 |
ctype = http.DetectContentType(p) |
| 1635 | 1647 |
} |
| 1636 |
- endStream := rws.handlerDone && len(p) == 0 |
|
| 1637 |
- rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
|
| 1648 |
+ var date string |
|
| 1649 |
+ if _, ok := rws.snapHeader["Date"]; !ok {
|
|
| 1650 |
+ // TODO(bradfitz): be faster here, like net/http? measure. |
|
| 1651 |
+ date = time.Now().UTC().Format(http.TimeFormat) |
|
| 1652 |
+ } |
|
| 1653 |
+ |
|
| 1654 |
+ for _, v := range rws.snapHeader["Trailer"] {
|
|
| 1655 |
+ foreachHeaderElement(v, rws.declareTrailer) |
|
| 1656 |
+ } |
|
| 1657 |
+ |
|
| 1658 |
+ endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp |
|
| 1659 |
+ err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
|
| 1638 | 1660 |
streamID: rws.stream.id, |
| 1639 | 1661 |
httpResCode: rws.status, |
| 1640 | 1662 |
h: rws.snapHeader, |
| 1641 | 1663 |
endStream: endStream, |
| 1642 | 1664 |
contentType: ctype, |
| 1643 | 1665 |
contentLength: clen, |
| 1644 |
- }, rws.frameWriteCh) |
|
| 1666 |
+ date: date, |
|
| 1667 |
+ }) |
|
| 1668 |
+ if err != nil {
|
|
| 1669 |
+ return 0, err |
|
| 1670 |
+ } |
|
| 1645 | 1671 |
if endStream {
|
| 1646 | 1672 |
return 0, nil |
| 1647 | 1673 |
} |
| 1648 | 1674 |
} |
| 1675 |
+ if isHeadResp {
|
|
| 1676 |
+ return len(p), nil |
|
| 1677 |
+ } |
|
| 1649 | 1678 |
if len(p) == 0 && !rws.handlerDone {
|
| 1650 | 1679 |
return 0, nil |
| 1651 | 1680 |
} |
| 1652 |
- curWrite := &rws.curWrite |
|
| 1653 |
- curWrite.streamID = rws.stream.id |
|
| 1654 |
- curWrite.p = p |
|
| 1655 |
- curWrite.endStream = rws.handlerDone |
|
| 1656 |
- if err := rws.conn.writeDataFromHandler(rws.stream, curWrite, rws.frameWriteCh); err != nil {
|
|
| 1657 |
- return 0, err |
|
| 1681 |
+ |
|
| 1682 |
+ if rws.handlerDone {
|
|
| 1683 |
+ rws.promoteUndeclaredTrailers() |
|
| 1684 |
+ } |
|
| 1685 |
+ |
|
| 1686 |
+ endStream := rws.handlerDone && !rws.hasTrailers() |
|
| 1687 |
+ if len(p) > 0 || endStream {
|
|
| 1688 |
+ // only send a 0 byte DATA frame if we're ending the stream. |
|
| 1689 |
+ if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
|
|
| 1690 |
+ return 0, err |
|
| 1691 |
+ } |
|
| 1692 |
+ } |
|
| 1693 |
+ |
|
| 1694 |
+ if rws.handlerDone && rws.hasTrailers() {
|
|
| 1695 |
+ err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
|
| 1696 |
+ streamID: rws.stream.id, |
|
| 1697 |
+ h: rws.handlerHeader, |
|
| 1698 |
+ trailers: rws.trailers, |
|
| 1699 |
+ endStream: true, |
|
| 1700 |
+ }) |
|
| 1701 |
+ return len(p), err |
|
| 1658 | 1702 |
} |
| 1659 | 1703 |
return len(p), nil |
| 1660 | 1704 |
} |
| 1661 | 1705 |
|
| 1706 |
+// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys |
|
| 1707 |
+// that, if present, signals that the map entry is actually for |
|
| 1708 |
+// the response trailers, and not the response headers. The prefix |
|
| 1709 |
+// is stripped after the ServeHTTP call finishes and the values are |
|
| 1710 |
+// sent in the trailers. |
|
| 1711 |
+// |
|
| 1712 |
+// This mechanism is intended only for trailers that are not known |
|
| 1713 |
+// prior to the headers being written. If the set of trailers is fixed |
|
| 1714 |
+// or known before the header is written, the normal Go trailers mechanism |
|
| 1715 |
+// is preferred: |
|
| 1716 |
+// https://golang.org/pkg/net/http/#ResponseWriter |
|
| 1717 |
+// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers |
|
| 1718 |
+const TrailerPrefix = "Trailer:" |
|
| 1719 |
+ |
|
| 1720 |
+// promoteUndeclaredTrailers permits http.Handlers to set trailers |
|
| 1721 |
+// after the header has already been flushed. Because the Go |
|
| 1722 |
+// ResponseWriter interface has no way to set Trailers (only the |
|
| 1723 |
+// Header), and because we didn't want to expand the ResponseWriter |
|
| 1724 |
+// interface, and because nobody used trailers, and because RFC 2616 |
|
| 1725 |
+// says you SHOULD (but not must) predeclare any trailers in the |
|
| 1726 |
+// header, the official ResponseWriter rules said trailers in Go must |
|
| 1727 |
+// be predeclared, and then we reuse the same ResponseWriter.Header() |
|
| 1728 |
+// map to mean both Headers and Trailers. When it's time to write the |
|
| 1729 |
+// Trailers, we pick out the fields of Headers that were declared as |
|
| 1730 |
+// trailers. That worked for a while, until we found the first major |
|
| 1731 |
+// user of Trailers in the wild: gRPC (using them only over http2), |
|
| 1732 |
+// and gRPC libraries permit setting trailers mid-stream without |
|
| 1733 |
+// predeclarnig them. So: change of plans. We still permit the old |
|
| 1734 |
+// way, but we also permit this hack: if a Header() key begins with |
|
| 1735 |
+// "Trailer:", the suffix of that key is a Trailer. Because ':' is an |
|
| 1736 |
+// invalid token byte anyway, there is no ambiguity. (And it's already |
|
| 1737 |
+// filtered out) It's mildly hacky, but not terrible. |
|
| 1738 |
+// |
|
| 1739 |
+// This method runs after the Handler is done and promotes any Header |
|
| 1740 |
+// fields to be trailers. |
|
| 1741 |
+func (rws *responseWriterState) promoteUndeclaredTrailers() {
|
|
| 1742 |
+ for k, vv := range rws.handlerHeader {
|
|
| 1743 |
+ if !strings.HasPrefix(k, TrailerPrefix) {
|
|
| 1744 |
+ continue |
|
| 1745 |
+ } |
|
| 1746 |
+ trailerKey := strings.TrimPrefix(k, TrailerPrefix) |
|
| 1747 |
+ rws.declareTrailer(trailerKey) |
|
| 1748 |
+ rws.handlerHeader[http.CanonicalHeaderKey(trailerKey)] = vv |
|
| 1749 |
+ } |
|
| 1750 |
+ |
|
| 1751 |
+ if len(rws.trailers) > 1 {
|
|
| 1752 |
+ sorter := sorterPool.Get().(*sorter) |
|
| 1753 |
+ sorter.SortStrings(rws.trailers) |
|
| 1754 |
+ sorterPool.Put(sorter) |
|
| 1755 |
+ } |
|
| 1756 |
+} |
|
| 1757 |
+ |
|
| 1662 | 1758 |
func (w *responseWriter) Flush() {
|
| 1663 | 1759 |
rws := w.rws |
| 1664 | 1760 |
if rws == nil {
|
| ... | ... |
@@ -1761,6 +2139,15 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, |
| 1761 | 1761 |
if !rws.wroteHeader {
|
| 1762 | 1762 |
w.WriteHeader(200) |
| 1763 | 1763 |
} |
| 1764 |
+ if !bodyAllowedForStatus(rws.status) {
|
|
| 1765 |
+ return 0, http.ErrBodyNotAllowed |
|
| 1766 |
+ } |
|
| 1767 |
+ rws.wroteBytes += int64(len(dataB)) + int64(len(dataS)) // only one can be set |
|
| 1768 |
+ if rws.sentContentLen != 0 && rws.wroteBytes > rws.sentContentLen {
|
|
| 1769 |
+ // TODO: send a RST_STREAM |
|
| 1770 |
+ return 0, errors.New("http2: handler wrote more than declared Content-Length")
|
|
| 1771 |
+ } |
|
| 1772 |
+ |
|
| 1764 | 1773 |
if dataB != nil {
|
| 1765 | 1774 |
return rws.bw.Write(dataB) |
| 1766 | 1775 |
} else {
|
| ... | ... |
@@ -1770,11 +2157,26 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, |
| 1770 | 1770 |
|
| 1771 | 1771 |
func (w *responseWriter) handlerDone() {
|
| 1772 | 1772 |
rws := w.rws |
| 1773 |
- if rws == nil {
|
|
| 1774 |
- panic("handlerDone called twice")
|
|
| 1775 |
- } |
|
| 1776 | 1773 |
rws.handlerDone = true |
| 1777 | 1774 |
w.Flush() |
| 1778 | 1775 |
w.rws = nil |
| 1779 | 1776 |
responseWriterStatePool.Put(rws) |
| 1780 | 1777 |
} |
| 1778 |
+ |
|
| 1779 |
+// foreachHeaderElement splits v according to the "#rule" construction |
|
| 1780 |
+// in RFC 2616 section 2.1 and calls fn for each non-empty element. |
|
| 1781 |
+func foreachHeaderElement(v string, fn func(string)) {
|
|
| 1782 |
+ v = textproto.TrimString(v) |
|
| 1783 |
+ if v == "" {
|
|
| 1784 |
+ return |
|
| 1785 |
+ } |
|
| 1786 |
+ if !strings.Contains(v, ",") {
|
|
| 1787 |
+ fn(v) |
|
| 1788 |
+ return |
|
| 1789 |
+ } |
|
| 1790 |
+ for _, f := range strings.Split(v, ",") {
|
|
| 1791 |
+ if f = textproto.TrimString(f); f != "" {
|
|
| 1792 |
+ fn(f) |
|
| 1793 |
+ } |
|
| 1794 |
+ } |
|
| 1795 |
+} |
| ... | ... |
@@ -1,55 +1,156 @@ |
| 1 |
-// Copyright 2015 The Go Authors. |
|
| 2 |
-// See https://go.googlesource.com/go/+/master/CONTRIBUTORS |
|
| 3 |
-// Licensed under the same terms as Go itself: |
|
| 4 |
-// https://go.googlesource.com/go/+/master/LICENSE |
|
| 1 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 2 |
+// Use of this source code is governed by a BSD-style |
|
| 3 |
+// license that can be found in the LICENSE file. |
|
| 4 |
+ |
|
| 5 |
+// Transport code. |
|
| 5 | 6 |
|
| 6 | 7 |
package http2 |
| 7 | 8 |
|
| 8 | 9 |
import ( |
| 9 | 10 |
"bufio" |
| 10 | 11 |
"bytes" |
| 12 |
+ "compress/gzip" |
|
| 11 | 13 |
"crypto/tls" |
| 12 | 14 |
"errors" |
| 13 | 15 |
"fmt" |
| 14 | 16 |
"io" |
| 17 |
+ "io/ioutil" |
|
| 15 | 18 |
"log" |
| 16 | 19 |
"net" |
| 17 | 20 |
"net/http" |
| 21 |
+ "sort" |
|
| 18 | 22 |
"strconv" |
| 19 | 23 |
"strings" |
| 20 | 24 |
"sync" |
| 25 |
+ "time" |
|
| 21 | 26 |
|
| 22 | 27 |
"golang.org/x/net/http2/hpack" |
| 23 | 28 |
) |
| 24 | 29 |
|
| 30 |
+const ( |
|
| 31 |
+ // transportDefaultConnFlow is how many connection-level flow control |
|
| 32 |
+ // tokens we give the server at start-up, past the default 64k. |
|
| 33 |
+ transportDefaultConnFlow = 1 << 30 |
|
| 34 |
+ |
|
| 35 |
+ // transportDefaultStreamFlow is how many stream-level flow |
|
| 36 |
+ // control tokens we announce to the peer, and how many bytes |
|
| 37 |
+ // we buffer per stream. |
|
| 38 |
+ transportDefaultStreamFlow = 4 << 20 |
|
| 39 |
+ |
|
| 40 |
+ // transportDefaultStreamMinRefresh is the minimum number of bytes we'll send |
|
| 41 |
+ // a stream-level WINDOW_UPDATE for at a time. |
|
| 42 |
+ transportDefaultStreamMinRefresh = 4 << 10 |
|
| 43 |
+ |
|
| 44 |
+ defaultUserAgent = "Go-http-client/2.0" |
|
| 45 |
+) |
|
| 46 |
+ |
|
| 47 |
+// Transport is an HTTP/2 Transport. |
|
| 48 |
+// |
|
| 49 |
+// A Transport internally caches connections to servers. It is safe |
|
| 50 |
+// for concurrent use by multiple goroutines. |
|
| 25 | 51 |
type Transport struct {
|
| 26 |
- Fallback http.RoundTripper |
|
| 52 |
+ // DialTLS specifies an optional dial function for creating |
|
| 53 |
+ // TLS connections for requests. |
|
| 54 |
+ // |
|
| 55 |
+ // If DialTLS is nil, tls.Dial is used. |
|
| 56 |
+ // |
|
| 57 |
+ // If the returned net.Conn has a ConnectionState method like tls.Conn, |
|
| 58 |
+ // it will be used to set http.Response.TLS. |
|
| 59 |
+ DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error) |
|
| 60 |
+ |
|
| 61 |
+ // TLSClientConfig specifies the TLS configuration to use with |
|
| 62 |
+ // tls.Client. If nil, the default configuration is used. |
|
| 63 |
+ TLSClientConfig *tls.Config |
|
| 64 |
+ |
|
| 65 |
+ // ConnPool optionally specifies an alternate connection pool to use. |
|
| 66 |
+ // If nil, the default is used. |
|
| 67 |
+ ConnPool ClientConnPool |
|
| 68 |
+ |
|
| 69 |
+ // DisableCompression, if true, prevents the Transport from |
|
| 70 |
+ // requesting compression with an "Accept-Encoding: gzip" |
|
| 71 |
+ // request header when the Request contains no existing |
|
| 72 |
+ // Accept-Encoding value. If the Transport requests gzip on |
|
| 73 |
+ // its own and gets a gzipped response, it's transparently |
|
| 74 |
+ // decoded in the Response.Body. However, if the user |
|
| 75 |
+ // explicitly requested gzip it is not automatically |
|
| 76 |
+ // uncompressed. |
|
| 77 |
+ DisableCompression bool |
|
| 78 |
+ |
|
| 79 |
+ // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to |
|
| 80 |
+ // send in the initial settings frame. It is how many bytes |
|
| 81 |
+ // of response headers are allow. Unlike the http2 spec, zero here |
|
| 82 |
+ // means to use a default limit (currently 10MB). If you actually |
|
| 83 |
+ // want to advertise an ulimited value to the peer, Transport |
|
| 84 |
+ // interprets the highest possible value here (0xffffffff or 1<<32-1) |
|
| 85 |
+ // to mean no limit. |
|
| 86 |
+ MaxHeaderListSize uint32 |
|
| 87 |
+ |
|
| 88 |
+ // t1, if non-nil, is the standard library Transport using |
|
| 89 |
+ // this transport. Its settings are used (but not its |
|
| 90 |
+ // RoundTrip method, etc). |
|
| 91 |
+ t1 *http.Transport |
|
| 92 |
+ |
|
| 93 |
+ connPoolOnce sync.Once |
|
| 94 |
+ connPoolOrDef ClientConnPool // non-nil version of ConnPool |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func (t *Transport) maxHeaderListSize() uint32 {
|
|
| 98 |
+ if t.MaxHeaderListSize == 0 {
|
|
| 99 |
+ return 10 << 20 |
|
| 100 |
+ } |
|
| 101 |
+ if t.MaxHeaderListSize == 0xffffffff {
|
|
| 102 |
+ return 0 |
|
| 103 |
+ } |
|
| 104 |
+ return t.MaxHeaderListSize |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func (t *Transport) disableCompression() bool {
|
|
| 108 |
+ return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+var errTransportVersion = errors.New("http2: ConfigureTransport is only supported starting at Go 1.6")
|
|
| 112 |
+ |
|
| 113 |
+// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. |
|
| 114 |
+// It requires Go 1.6 or later and returns an error if the net/http package is too old |
|
| 115 |
+// or if t1 has already been HTTP/2-enabled. |
|
| 116 |
+func ConfigureTransport(t1 *http.Transport) error {
|
|
| 117 |
+ _, err := configureTransport(t1) // in configure_transport.go (go1.6) or not_go16.go |
|
| 118 |
+ return err |
|
| 119 |
+} |
|
| 27 | 120 |
|
| 28 |
- // TODO: remove this and make more general with a TLS dial hook, like http |
|
| 29 |
- InsecureTLSDial bool |
|
| 121 |
+func (t *Transport) connPool() ClientConnPool {
|
|
| 122 |
+ t.connPoolOnce.Do(t.initConnPool) |
|
| 123 |
+ return t.connPoolOrDef |
|
| 124 |
+} |
|
| 30 | 125 |
|
| 31 |
- connMu sync.Mutex |
|
| 32 |
- conns map[string][]*clientConn // key is host:port |
|
| 126 |
+func (t *Transport) initConnPool() {
|
|
| 127 |
+ if t.ConnPool != nil {
|
|
| 128 |
+ t.connPoolOrDef = t.ConnPool |
|
| 129 |
+ } else {
|
|
| 130 |
+ t.connPoolOrDef = &clientConnPool{t: t}
|
|
| 131 |
+ } |
|
| 33 | 132 |
} |
| 34 | 133 |
|
| 35 |
-type clientConn struct {
|
|
| 134 |
+// ClientConn is the state of a single HTTP/2 client connection to an |
|
| 135 |
+// HTTP/2 server. |
|
| 136 |
+type ClientConn struct {
|
|
| 36 | 137 |
t *Transport |
| 37 |
- tconn *tls.Conn |
|
| 38 |
- tlsState *tls.ConnectionState |
|
| 39 |
- connKey []string // key(s) this connection is cached in, in t.conns |
|
| 138 |
+ tconn net.Conn // usually *tls.Conn, except specialized impls |
|
| 139 |
+ tlsState *tls.ConnectionState // nil only for specialized impls |
|
| 40 | 140 |
|
| 141 |
+ // readLoop goroutine fields: |
|
| 41 | 142 |
readerDone chan struct{} // closed on error
|
| 42 | 143 |
readerErr error // set before readerDone is closed |
| 43 |
- hdec *hpack.Decoder |
|
| 44 |
- nextRes *http.Response |
|
| 45 | 144 |
|
| 46 |
- mu sync.Mutex |
|
| 145 |
+ mu sync.Mutex // guards following |
|
| 146 |
+ cond *sync.Cond // hold mu; broadcast on flow/closed changes |
|
| 147 |
+ flow flow // our conn-level flow control quota (cs.flow is per stream) |
|
| 148 |
+ inflow flow // peer's conn-level flow control |
|
| 47 | 149 |
closed bool |
| 48 |
- goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received |
|
| 49 |
- streams map[uint32]*clientStream |
|
| 150 |
+ goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received |
|
| 151 |
+ streams map[uint32]*clientStream // client-initiated |
|
| 50 | 152 |
nextStreamID uint32 |
| 51 | 153 |
bw *bufio.Writer |
| 52 |
- werr error // first write error that has occurred |
|
| 53 | 154 |
br *bufio.Reader |
| 54 | 155 |
fr *Framer |
| 55 | 156 |
// Settings from peer: |
| ... | ... |
@@ -58,13 +159,78 @@ type clientConn struct {
|
| 58 | 58 |
initialWindowSize uint32 |
| 59 | 59 |
hbuf bytes.Buffer // HPACK encoder writes into this |
| 60 | 60 |
henc *hpack.Encoder |
| 61 |
+ freeBuf [][]byte |
|
| 62 |
+ |
|
| 63 |
+ wmu sync.Mutex // held while writing; acquire AFTER mu if holding both |
|
| 64 |
+ werr error // first write error that has occurred |
|
| 61 | 65 |
} |
| 62 | 66 |
|
| 67 |
+// clientStream is the state for a single HTTP/2 stream. One of these |
|
| 68 |
+// is created for each Transport.RoundTrip call. |
|
| 63 | 69 |
type clientStream struct {
|
| 64 |
- ID uint32 |
|
| 65 |
- resc chan resAndError |
|
| 66 |
- pw *io.PipeWriter |
|
| 67 |
- pr *io.PipeReader |
|
| 70 |
+ cc *ClientConn |
|
| 71 |
+ req *http.Request |
|
| 72 |
+ ID uint32 |
|
| 73 |
+ resc chan resAndError |
|
| 74 |
+ bufPipe pipe // buffered pipe with the flow-controlled response payload |
|
| 75 |
+ requestedGzip bool |
|
| 76 |
+ |
|
| 77 |
+ flow flow // guarded by cc.mu |
|
| 78 |
+ inflow flow // guarded by cc.mu |
|
| 79 |
+ bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read |
|
| 80 |
+ readErr error // sticky read error; owned by transportResponseBody.Read |
|
| 81 |
+ stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu |
|
| 82 |
+ |
|
| 83 |
+ peerReset chan struct{} // closed on peer reset
|
|
| 84 |
+ resetErr error // populated before peerReset is closed |
|
| 85 |
+ |
|
| 86 |
+ done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
|
|
| 87 |
+ |
|
| 88 |
+ // owned by clientConnReadLoop: |
|
| 89 |
+ pastHeaders bool // got first MetaHeadersFrame (actual headers) |
|
| 90 |
+ pastTrailers bool // got optional second MetaHeadersFrame (trailers) |
|
| 91 |
+ |
|
| 92 |
+ trailer http.Header // accumulated trailers |
|
| 93 |
+ resTrailer *http.Header // client's Response.Trailer |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// awaitRequestCancel runs in its own goroutine and waits for the user |
|
| 97 |
+// to either cancel a RoundTrip request (using the provided |
|
| 98 |
+// Request.Cancel channel), or for the request to be done (any way it |
|
| 99 |
+// might be removed from the cc.streams map: peer reset, successful |
|
| 100 |
+// completion, TCP connection breakage, etc) |
|
| 101 |
+func (cs *clientStream) awaitRequestCancel(cancel <-chan struct{}) {
|
|
| 102 |
+ if cancel == nil {
|
|
| 103 |
+ return |
|
| 104 |
+ } |
|
| 105 |
+ select {
|
|
| 106 |
+ case <-cancel: |
|
| 107 |
+ cs.bufPipe.CloseWithError(errRequestCanceled) |
|
| 108 |
+ cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) |
|
| 109 |
+ case <-cs.done: |
|
| 110 |
+ } |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+// checkReset reports any error sent in a RST_STREAM frame by the |
|
| 114 |
+// server. |
|
| 115 |
+func (cs *clientStream) checkReset() error {
|
|
| 116 |
+ select {
|
|
| 117 |
+ case <-cs.peerReset: |
|
| 118 |
+ return cs.resetErr |
|
| 119 |
+ default: |
|
| 120 |
+ return nil |
|
| 121 |
+ } |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func (cs *clientStream) abortRequestBodyWrite(err error) {
|
|
| 125 |
+ if err == nil {
|
|
| 126 |
+ panic("nil error")
|
|
| 127 |
+ } |
|
| 128 |
+ cc := cs.cc |
|
| 129 |
+ cc.mu.Lock() |
|
| 130 |
+ cs.stopReqBody = err |
|
| 131 |
+ cc.cond.Broadcast() |
|
| 132 |
+ cc.mu.Unlock() |
|
| 68 | 133 |
} |
| 69 | 134 |
|
| 70 | 135 |
type stickyErrWriter struct {
|
| ... | ... |
@@ -81,30 +247,49 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) {
|
| 81 | 81 |
return |
| 82 | 82 |
} |
| 83 | 83 |
|
| 84 |
+var ErrNoCachedConn = errors.New("http2: no cached connection was available")
|
|
| 85 |
+ |
|
| 86 |
+// RoundTripOpt are options for the Transport.RoundTripOpt method. |
|
| 87 |
+type RoundTripOpt struct {
|
|
| 88 |
+ // OnlyCachedConn controls whether RoundTripOpt may |
|
| 89 |
+ // create a new TCP connection. If set true and |
|
| 90 |
+ // no cached connection is available, RoundTripOpt |
|
| 91 |
+ // will return ErrNoCachedConn. |
|
| 92 |
+ OnlyCachedConn bool |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 84 | 95 |
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
| 85 |
- if req.URL.Scheme != "https" {
|
|
| 86 |
- if t.Fallback == nil {
|
|
| 87 |
- return nil, errors.New("http2: unsupported scheme and no Fallback")
|
|
| 88 |
- } |
|
| 89 |
- return t.Fallback.RoundTrip(req) |
|
| 96 |
+ return t.RoundTripOpt(req, RoundTripOpt{})
|
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+// authorityAddr returns a given authority (a host/IP, or host:port / ip:port) |
|
| 100 |
+// and returns a host:port. The port 443 is added if needed. |
|
| 101 |
+func authorityAddr(authority string) (addr string) {
|
|
| 102 |
+ if _, _, err := net.SplitHostPort(authority); err == nil {
|
|
| 103 |
+ return authority |
|
| 90 | 104 |
} |
| 105 |
+ return net.JoinHostPort(authority, "443") |
|
| 106 |
+} |
|
| 91 | 107 |
|
| 92 |
- host, port, err := net.SplitHostPort(req.URL.Host) |
|
| 93 |
- if err != nil {
|
|
| 94 |
- host = req.URL.Host |
|
| 95 |
- port = "443" |
|
| 108 |
+// RoundTripOpt is like RoundTrip, but takes options. |
|
| 109 |
+func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
|
|
| 110 |
+ if req.URL.Scheme != "https" {
|
|
| 111 |
+ return nil, errors.New("http2: unsupported scheme")
|
|
| 96 | 112 |
} |
| 97 | 113 |
|
| 114 |
+ addr := authorityAddr(req.URL.Host) |
|
| 98 | 115 |
for {
|
| 99 |
- cc, err := t.getClientConn(host, port) |
|
| 116 |
+ cc, err := t.connPool().GetClientConn(req, addr) |
|
| 100 | 117 |
if err != nil {
|
| 118 |
+ t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
|
|
| 101 | 119 |
return nil, err |
| 102 | 120 |
} |
| 103 |
- res, err := cc.roundTrip(req) |
|
| 104 |
- if shouldRetryRequest(err) { // TODO: or clientconn is overloaded (too many outstanding requests)?
|
|
| 121 |
+ res, err := cc.RoundTrip(req) |
|
| 122 |
+ if shouldRetryRequest(req, err) {
|
|
| 105 | 123 |
continue |
| 106 | 124 |
} |
| 107 | 125 |
if err != nil {
|
| 126 |
+ t.vlogf("RoundTrip failure: %v", err)
|
|
| 108 | 127 |
return nil, err |
| 109 | 128 |
} |
| 110 | 129 |
return res, nil |
| ... | ... |
@@ -115,106 +300,96 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
| 115 | 115 |
// connected from previous requests but are now sitting idle. |
| 116 | 116 |
// It does not interrupt any connections currently in use. |
| 117 | 117 |
func (t *Transport) CloseIdleConnections() {
|
| 118 |
- t.connMu.Lock() |
|
| 119 |
- defer t.connMu.Unlock() |
|
| 120 |
- for _, vv := range t.conns {
|
|
| 121 |
- for _, cc := range vv {
|
|
| 122 |
- cc.closeIfIdle() |
|
| 123 |
- } |
|
| 118 |
+ if cp, ok := t.connPool().(*clientConnPool); ok {
|
|
| 119 |
+ cp.closeIdleConnections() |
|
| 124 | 120 |
} |
| 125 | 121 |
} |
| 126 | 122 |
|
| 127 |
-var errClientConnClosed = errors.New("http2: client conn is closed")
|
|
| 123 |
+var ( |
|
| 124 |
+ errClientConnClosed = errors.New("http2: client conn is closed")
|
|
| 125 |
+ errClientConnUnusable = errors.New("http2: client conn not usable")
|
|
| 126 |
+) |
|
| 128 | 127 |
|
| 129 |
-func shouldRetryRequest(err error) bool {
|
|
| 130 |
- // TODO: or GOAWAY graceful shutdown stuff |
|
| 131 |
- return err == errClientConnClosed |
|
| 128 |
+func shouldRetryRequest(req *http.Request, err error) bool {
|
|
| 129 |
+ // TODO: retry GET requests (no bodies) more aggressively, if shutdown |
|
| 130 |
+ // before response. |
|
| 131 |
+ return err == errClientConnUnusable |
|
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 |
-func (t *Transport) removeClientConn(cc *clientConn) {
|
|
| 135 |
- t.connMu.Lock() |
|
| 136 |
- defer t.connMu.Unlock() |
|
| 137 |
- for _, key := range cc.connKey {
|
|
| 138 |
- vv, ok := t.conns[key] |
|
| 139 |
- if !ok {
|
|
| 140 |
- continue |
|
| 141 |
- } |
|
| 142 |
- newList := filterOutClientConn(vv, cc) |
|
| 143 |
- if len(newList) > 0 {
|
|
| 144 |
- t.conns[key] = newList |
|
| 145 |
- } else {
|
|
| 146 |
- delete(t.conns, key) |
|
| 147 |
- } |
|
| 134 |
+func (t *Transport) dialClientConn(addr string) (*ClientConn, error) {
|
|
| 135 |
+ host, _, err := net.SplitHostPort(addr) |
|
| 136 |
+ if err != nil {
|
|
| 137 |
+ return nil, err |
|
| 148 | 138 |
} |
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-func filterOutClientConn(in []*clientConn, exclude *clientConn) []*clientConn {
|
|
| 152 |
- out := in[:0] |
|
| 153 |
- for _, v := range in {
|
|
| 154 |
- if v != exclude {
|
|
| 155 |
- out = append(out, v) |
|
| 156 |
- } |
|
| 139 |
+ tconn, err := t.dialTLS()("tcp", addr, t.newTLSConfig(host))
|
|
| 140 |
+ if err != nil {
|
|
| 141 |
+ return nil, err |
|
| 157 | 142 |
} |
| 158 |
- return out |
|
| 143 |
+ return t.NewClientConn(tconn) |
|
| 159 | 144 |
} |
| 160 | 145 |
|
| 161 |
-func (t *Transport) getClientConn(host, port string) (*clientConn, error) {
|
|
| 162 |
- t.connMu.Lock() |
|
| 163 |
- defer t.connMu.Unlock() |
|
| 164 |
- |
|
| 165 |
- key := net.JoinHostPort(host, port) |
|
| 166 |
- |
|
| 167 |
- for _, cc := range t.conns[key] {
|
|
| 168 |
- if cc.canTakeNewRequest() {
|
|
| 169 |
- return cc, nil |
|
| 170 |
- } |
|
| 146 |
+func (t *Transport) newTLSConfig(host string) *tls.Config {
|
|
| 147 |
+ cfg := new(tls.Config) |
|
| 148 |
+ if t.TLSClientConfig != nil {
|
|
| 149 |
+ *cfg = *t.TLSClientConfig |
|
| 171 | 150 |
} |
| 172 |
- if t.conns == nil {
|
|
| 173 |
- t.conns = make(map[string][]*clientConn) |
|
| 151 |
+ if !strSliceContains(cfg.NextProtos, NextProtoTLS) {
|
|
| 152 |
+ cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)
|
|
| 174 | 153 |
} |
| 175 |
- cc, err := t.newClientConn(host, port, key) |
|
| 176 |
- if err != nil {
|
|
| 177 |
- return nil, err |
|
| 154 |
+ if cfg.ServerName == "" {
|
|
| 155 |
+ cfg.ServerName = host |
|
| 178 | 156 |
} |
| 179 |
- t.conns[key] = append(t.conns[key], cc) |
|
| 180 |
- return cc, nil |
|
| 157 |
+ return cfg |
|
| 181 | 158 |
} |
| 182 | 159 |
|
| 183 |
-func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
|
|
| 184 |
- cfg := &tls.Config{
|
|
| 185 |
- ServerName: host, |
|
| 186 |
- NextProtos: []string{NextProtoTLS},
|
|
| 187 |
- InsecureSkipVerify: t.InsecureTLSDial, |
|
| 160 |
+func (t *Transport) dialTLS() func(string, string, *tls.Config) (net.Conn, error) {
|
|
| 161 |
+ if t.DialTLS != nil {
|
|
| 162 |
+ return t.DialTLS |
|
| 188 | 163 |
} |
| 189 |
- tconn, err := tls.Dial("tcp", net.JoinHostPort(host, port), cfg)
|
|
| 164 |
+ return t.dialTLSDefault |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+func (t *Transport) dialTLSDefault(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
|
| 168 |
+ cn, err := tls.Dial(network, addr, cfg) |
|
| 190 | 169 |
if err != nil {
|
| 191 | 170 |
return nil, err |
| 192 | 171 |
} |
| 193 |
- if err := tconn.Handshake(); err != nil {
|
|
| 172 |
+ if err := cn.Handshake(); err != nil {
|
|
| 194 | 173 |
return nil, err |
| 195 | 174 |
} |
| 196 |
- if !t.InsecureTLSDial {
|
|
| 197 |
- if err := tconn.VerifyHostname(cfg.ServerName); err != nil {
|
|
| 175 |
+ if !cfg.InsecureSkipVerify {
|
|
| 176 |
+ if err := cn.VerifyHostname(cfg.ServerName); err != nil {
|
|
| 198 | 177 |
return nil, err |
| 199 | 178 |
} |
| 200 | 179 |
} |
| 201 |
- state := tconn.ConnectionState() |
|
| 180 |
+ state := cn.ConnectionState() |
|
| 202 | 181 |
if p := state.NegotiatedProtocol; p != NextProtoTLS {
|
| 203 |
- // TODO(bradfitz): fall back to Fallback |
|
| 204 |
- return nil, fmt.Errorf("bad protocol: %v", p)
|
|
| 182 |
+ return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS)
|
|
| 205 | 183 |
} |
| 206 | 184 |
if !state.NegotiatedProtocolIsMutual {
|
| 207 |
- return nil, errors.New("could not negotiate protocol mutually")
|
|
| 185 |
+ return nil, errors.New("http2: could not negotiate protocol mutually")
|
|
| 186 |
+ } |
|
| 187 |
+ return cn, nil |
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+// disableKeepAlives reports whether connections should be closed as |
|
| 191 |
+// soon as possible after handling the first request. |
|
| 192 |
+func (t *Transport) disableKeepAlives() bool {
|
|
| 193 |
+ return t.t1 != nil && t.t1.DisableKeepAlives |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
|
| 197 |
+ if VerboseLogs {
|
|
| 198 |
+ t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr())
|
|
| 208 | 199 |
} |
| 209 |
- if _, err := tconn.Write(clientPreface); err != nil {
|
|
| 200 |
+ if _, err := c.Write(clientPreface); err != nil {
|
|
| 201 |
+ t.vlogf("client preface write error: %v", err)
|
|
| 210 | 202 |
return nil, err |
| 211 | 203 |
} |
| 212 | 204 |
|
| 213 |
- cc := &clientConn{
|
|
| 205 |
+ cc := &ClientConn{
|
|
| 214 | 206 |
t: t, |
| 215 |
- tconn: tconn, |
|
| 216 |
- connKey: []string{key}, // TODO: cert's validated hostnames too
|
|
| 217 |
- tlsState: &state, |
|
| 207 |
+ tconn: c, |
|
| 218 | 208 |
readerDone: make(chan struct{}),
|
| 219 | 209 |
nextStreamID: 1, |
| 220 | 210 |
maxFrameSize: 16 << 10, // spec default |
| ... | ... |
@@ -222,14 +397,36 @@ func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
|
| 222 | 222 |
maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. |
| 223 | 223 |
streams: make(map[uint32]*clientStream), |
| 224 | 224 |
} |
| 225 |
- cc.bw = bufio.NewWriter(stickyErrWriter{tconn, &cc.werr})
|
|
| 226 |
- cc.br = bufio.NewReader(tconn) |
|
| 225 |
+ cc.cond = sync.NewCond(&cc.mu) |
|
| 226 |
+ cc.flow.add(int32(initialWindowSize)) |
|
| 227 |
+ |
|
| 228 |
+ // TODO: adjust this writer size to account for frame size + |
|
| 229 |
+ // MTU + crypto/tls record padding. |
|
| 230 |
+ cc.bw = bufio.NewWriter(stickyErrWriter{c, &cc.werr})
|
|
| 231 |
+ cc.br = bufio.NewReader(c) |
|
| 227 | 232 |
cc.fr = NewFramer(cc.bw, cc.br) |
| 233 |
+ cc.fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil) |
|
| 234 |
+ cc.fr.MaxHeaderListSize = t.maxHeaderListSize() |
|
| 235 |
+ |
|
| 236 |
+ // TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on |
|
| 237 |
+ // henc in response to SETTINGS frames? |
|
| 228 | 238 |
cc.henc = hpack.NewEncoder(&cc.hbuf) |
| 229 | 239 |
|
| 230 |
- cc.fr.WriteSettings() |
|
| 231 |
- // TODO: re-send more conn-level flow control tokens when server uses all these. |
|
| 232 |
- cc.fr.WriteWindowUpdate(0, 1<<30) // um, 0x7fffffff doesn't work to Google? it hangs? |
|
| 240 |
+ if cs, ok := c.(connectionStater); ok {
|
|
| 241 |
+ state := cs.ConnectionState() |
|
| 242 |
+ cc.tlsState = &state |
|
| 243 |
+ } |
|
| 244 |
+ |
|
| 245 |
+ initialSettings := []Setting{
|
|
| 246 |
+ {ID: SettingEnablePush, Val: 0},
|
|
| 247 |
+ {ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow},
|
|
| 248 |
+ } |
|
| 249 |
+ if max := t.maxHeaderListSize(); max != 0 {
|
|
| 250 |
+ initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max})
|
|
| 251 |
+ } |
|
| 252 |
+ cc.fr.WriteSettings(initialSettings...) |
|
| 253 |
+ cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow) |
|
| 254 |
+ cc.inflow.add(transportDefaultConnFlow + initialWindowSize) |
|
| 233 | 255 |
cc.bw.Flush() |
| 234 | 256 |
if cc.werr != nil {
|
| 235 | 257 |
return nil, cc.werr |
| ... | ... |
@@ -256,33 +453,35 @@ func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
|
| 256 | 256 |
case SettingInitialWindowSize: |
| 257 | 257 |
cc.initialWindowSize = s.Val |
| 258 | 258 |
default: |
| 259 |
- // TODO(bradfitz): handle more |
|
| 260 |
- log.Printf("Unhandled Setting: %v", s)
|
|
| 259 |
+ // TODO(bradfitz): handle more; at least SETTINGS_HEADER_TABLE_SIZE? |
|
| 260 |
+ t.vlogf("Unhandled Setting: %v", s)
|
|
| 261 | 261 |
} |
| 262 | 262 |
return nil |
| 263 | 263 |
}) |
| 264 |
- // TODO: figure out henc size |
|
| 265 |
- cc.hdec = hpack.NewDecoder(initialHeaderTableSize, cc.onNewHeaderField) |
|
| 266 | 264 |
|
| 267 | 265 |
go cc.readLoop() |
| 268 | 266 |
return cc, nil |
| 269 | 267 |
} |
| 270 | 268 |
|
| 271 |
-func (cc *clientConn) setGoAway(f *GoAwayFrame) {
|
|
| 269 |
+func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
|
|
| 272 | 270 |
cc.mu.Lock() |
| 273 | 271 |
defer cc.mu.Unlock() |
| 274 | 272 |
cc.goAway = f |
| 275 | 273 |
} |
| 276 | 274 |
|
| 277 |
-func (cc *clientConn) canTakeNewRequest() bool {
|
|
| 275 |
+func (cc *ClientConn) CanTakeNewRequest() bool {
|
|
| 278 | 276 |
cc.mu.Lock() |
| 279 | 277 |
defer cc.mu.Unlock() |
| 280 |
- return cc.goAway == nil && |
|
| 278 |
+ return cc.canTakeNewRequestLocked() |
|
| 279 |
+} |
|
| 280 |
+ |
|
| 281 |
+func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
|
| 282 |
+ return cc.goAway == nil && !cc.closed && |
|
| 281 | 283 |
int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) && |
| 282 | 284 |
cc.nextStreamID < 2147483647 |
| 283 | 285 |
} |
| 284 | 286 |
|
| 285 |
-func (cc *clientConn) closeIfIdle() {
|
|
| 287 |
+func (cc *ClientConn) closeIfIdle() {
|
|
| 286 | 288 |
cc.mu.Lock() |
| 287 | 289 |
if len(cc.streams) > 0 {
|
| 288 | 290 |
cc.mu.Unlock() |
| ... | ... |
@@ -295,96 +494,533 @@ func (cc *clientConn) closeIfIdle() {
|
| 295 | 295 |
cc.tconn.Close() |
| 296 | 296 |
} |
| 297 | 297 |
|
| 298 |
-func (cc *clientConn) roundTrip(req *http.Request) (*http.Response, error) {
|
|
| 298 |
+const maxAllocFrameSize = 512 << 10 |
|
| 299 |
+ |
|
| 300 |
+// frameBuffer returns a scratch buffer suitable for writing DATA frames. |
|
| 301 |
+// They're capped at the min of the peer's max frame size or 512KB |
|
| 302 |
+// (kinda arbitrarily), but definitely capped so we don't allocate 4GB |
|
| 303 |
+// bufers. |
|
| 304 |
+func (cc *ClientConn) frameScratchBuffer() []byte {
|
|
| 299 | 305 |
cc.mu.Lock() |
| 306 |
+ size := cc.maxFrameSize |
|
| 307 |
+ if size > maxAllocFrameSize {
|
|
| 308 |
+ size = maxAllocFrameSize |
|
| 309 |
+ } |
|
| 310 |
+ for i, buf := range cc.freeBuf {
|
|
| 311 |
+ if len(buf) >= int(size) {
|
|
| 312 |
+ cc.freeBuf[i] = nil |
|
| 313 |
+ cc.mu.Unlock() |
|
| 314 |
+ return buf[:size] |
|
| 315 |
+ } |
|
| 316 |
+ } |
|
| 317 |
+ cc.mu.Unlock() |
|
| 318 |
+ return make([]byte, size) |
|
| 319 |
+} |
|
| 320 |
+ |
|
| 321 |
+func (cc *ClientConn) putFrameScratchBuffer(buf []byte) {
|
|
| 322 |
+ cc.mu.Lock() |
|
| 323 |
+ defer cc.mu.Unlock() |
|
| 324 |
+ const maxBufs = 4 // arbitrary; 4 concurrent requests per conn? investigate. |
|
| 325 |
+ if len(cc.freeBuf) < maxBufs {
|
|
| 326 |
+ cc.freeBuf = append(cc.freeBuf, buf) |
|
| 327 |
+ return |
|
| 328 |
+ } |
|
| 329 |
+ for i, old := range cc.freeBuf {
|
|
| 330 |
+ if old == nil {
|
|
| 331 |
+ cc.freeBuf[i] = buf |
|
| 332 |
+ return |
|
| 333 |
+ } |
|
| 334 |
+ } |
|
| 335 |
+ // forget about it. |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+// errRequestCanceled is a copy of net/http's errRequestCanceled because it's not |
|
| 339 |
+// exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests. |
|
| 340 |
+var errRequestCanceled = errors.New("net/http: request canceled")
|
|
| 341 |
+ |
|
| 342 |
+func commaSeparatedTrailers(req *http.Request) (string, error) {
|
|
| 343 |
+ keys := make([]string, 0, len(req.Trailer)) |
|
| 344 |
+ for k := range req.Trailer {
|
|
| 345 |
+ k = http.CanonicalHeaderKey(k) |
|
| 346 |
+ switch k {
|
|
| 347 |
+ case "Transfer-Encoding", "Trailer", "Content-Length": |
|
| 348 |
+ return "", &badStringError{"invalid Trailer key", k}
|
|
| 349 |
+ } |
|
| 350 |
+ keys = append(keys, k) |
|
| 351 |
+ } |
|
| 352 |
+ if len(keys) > 0 {
|
|
| 353 |
+ sort.Strings(keys) |
|
| 354 |
+ // TODO: could do better allocation-wise here, but trailers are rare, |
|
| 355 |
+ // so being lazy for now. |
|
| 356 |
+ return strings.Join(keys, ","), nil |
|
| 357 |
+ } |
|
| 358 |
+ return "", nil |
|
| 359 |
+} |
|
| 360 |
+ |
|
| 361 |
+func (cc *ClientConn) responseHeaderTimeout() time.Duration {
|
|
| 362 |
+ if cc.t.t1 != nil {
|
|
| 363 |
+ return cc.t.t1.ResponseHeaderTimeout |
|
| 364 |
+ } |
|
| 365 |
+ // No way to do this (yet?) with just an http2.Transport. Probably |
|
| 366 |
+ // no need. Request.Cancel this is the new way. We only need to support |
|
| 367 |
+ // this for compatibility with the old http.Transport fields when |
|
| 368 |
+ // we're doing transparent http2. |
|
| 369 |
+ return 0 |
|
| 370 |
+} |
|
| 371 |
+ |
|
| 372 |
+// checkConnHeaders checks whether req has any invalid connection-level headers. |
|
| 373 |
+// per RFC 7540 section 8.1.2.2: Connection-Specific Header Fields. |
|
| 374 |
+// Certain headers are special-cased as okay but not transmitted later. |
|
| 375 |
+func checkConnHeaders(req *http.Request) error {
|
|
| 376 |
+ if v := req.Header.Get("Upgrade"); v != "" {
|
|
| 377 |
+ return errors.New("http2: invalid Upgrade request header")
|
|
| 378 |
+ } |
|
| 379 |
+ if v := req.Header.Get("Transfer-Encoding"); (v != "" && v != "chunked") || len(req.Header["Transfer-Encoding"]) > 1 {
|
|
| 380 |
+ return errors.New("http2: invalid Transfer-Encoding request header")
|
|
| 381 |
+ } |
|
| 382 |
+ if v := req.Header.Get("Connection"); (v != "" && v != "close" && v != "keep-alive") || len(req.Header["Connection"]) > 1 {
|
|
| 383 |
+ return errors.New("http2: invalid Connection request header")
|
|
| 384 |
+ } |
|
| 385 |
+ return nil |
|
| 386 |
+} |
|
| 387 |
+ |
|
| 388 |
+func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
| 389 |
+ if err := checkConnHeaders(req); err != nil {
|
|
| 390 |
+ return nil, err |
|
| 391 |
+ } |
|
| 392 |
+ |
|
| 393 |
+ trailers, err := commaSeparatedTrailers(req) |
|
| 394 |
+ if err != nil {
|
|
| 395 |
+ return nil, err |
|
| 396 |
+ } |
|
| 397 |
+ hasTrailers := trailers != "" |
|
| 398 |
+ |
|
| 399 |
+ var body io.Reader = req.Body |
|
| 400 |
+ contentLen := req.ContentLength |
|
| 401 |
+ if req.Body != nil && contentLen == 0 {
|
|
| 402 |
+ // Test to see if it's actually zero or just unset. |
|
| 403 |
+ var buf [1]byte |
|
| 404 |
+ n, rerr := io.ReadFull(body, buf[:]) |
|
| 405 |
+ if rerr != nil && rerr != io.EOF {
|
|
| 406 |
+ contentLen = -1 |
|
| 407 |
+ body = errorReader{rerr}
|
|
| 408 |
+ } else if n == 1 {
|
|
| 409 |
+ // Oh, guess there is data in this Body Reader after all. |
|
| 410 |
+ // The ContentLength field just wasn't set. |
|
| 411 |
+ // Stich the Body back together again, re-attaching our |
|
| 412 |
+ // consumed byte. |
|
| 413 |
+ contentLen = -1 |
|
| 414 |
+ body = io.MultiReader(bytes.NewReader(buf[:]), body) |
|
| 415 |
+ } else {
|
|
| 416 |
+ // Body is actually empty. |
|
| 417 |
+ body = nil |
|
| 418 |
+ } |
|
| 419 |
+ } |
|
| 300 | 420 |
|
| 301 |
- if cc.closed {
|
|
| 421 |
+ cc.mu.Lock() |
|
| 422 |
+ if cc.closed || !cc.canTakeNewRequestLocked() {
|
|
| 302 | 423 |
cc.mu.Unlock() |
| 303 |
- return nil, errClientConnClosed |
|
| 424 |
+ return nil, errClientConnUnusable |
|
| 304 | 425 |
} |
| 305 | 426 |
|
| 306 | 427 |
cs := cc.newStream() |
| 307 |
- hasBody := false // TODO |
|
| 428 |
+ cs.req = req |
|
| 429 |
+ hasBody := body != nil |
|
| 430 |
+ |
|
| 431 |
+ // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? |
|
| 432 |
+ if !cc.t.disableCompression() && |
|
| 433 |
+ req.Header.Get("Accept-Encoding") == "" &&
|
|
| 434 |
+ req.Header.Get("Range") == "" &&
|
|
| 435 |
+ req.Method != "HEAD" {
|
|
| 436 |
+ // Request gzip only, not deflate. Deflate is ambiguous and |
|
| 437 |
+ // not as universally supported anyway. |
|
| 438 |
+ // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 |
|
| 439 |
+ // |
|
| 440 |
+ // Note that we don't request this for HEAD requests, |
|
| 441 |
+ // due to a bug in nginx: |
|
| 442 |
+ // http://trac.nginx.org/nginx/ticket/358 |
|
| 443 |
+ // https://golang.org/issue/5522 |
|
| 444 |
+ // |
|
| 445 |
+ // We don't request gzip if the request is for a range, since |
|
| 446 |
+ // auto-decoding a portion of a gzipped document will just fail |
|
| 447 |
+ // anyway. See https://golang.org/issue/8923 |
|
| 448 |
+ cs.requestedGzip = true |
|
| 449 |
+ } |
|
| 308 | 450 |
|
| 309 |
- // we send: HEADERS[+CONTINUATION] + (DATA?) |
|
| 310 |
- hdrs := cc.encodeHeaders(req) |
|
| 311 |
- first := true |
|
| 312 |
- for len(hdrs) > 0 {
|
|
| 451 |
+ // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is
|
|
| 452 |
+ // sent by writeRequestBody below, along with any Trailers, |
|
| 453 |
+ // again in form HEADERS{1}, CONTINUATION{0,})
|
|
| 454 |
+ hdrs := cc.encodeHeaders(req, cs.requestedGzip, trailers, contentLen) |
|
| 455 |
+ cc.wmu.Lock() |
|
| 456 |
+ endStream := !hasBody && !hasTrailers |
|
| 457 |
+ werr := cc.writeHeaders(cs.ID, endStream, hdrs) |
|
| 458 |
+ cc.wmu.Unlock() |
|
| 459 |
+ cc.mu.Unlock() |
|
| 460 |
+ |
|
| 461 |
+ if werr != nil {
|
|
| 462 |
+ if hasBody {
|
|
| 463 |
+ req.Body.Close() // per RoundTripper contract |
|
| 464 |
+ } |
|
| 465 |
+ cc.forgetStreamID(cs.ID) |
|
| 466 |
+ // Don't bother sending a RST_STREAM (our write already failed; |
|
| 467 |
+ // no need to keep writing) |
|
| 468 |
+ return nil, werr |
|
| 469 |
+ } |
|
| 470 |
+ |
|
| 471 |
+ var respHeaderTimer <-chan time.Time |
|
| 472 |
+ var bodyCopyErrc chan error // result of body copy |
|
| 473 |
+ if hasBody {
|
|
| 474 |
+ bodyCopyErrc = make(chan error, 1) |
|
| 475 |
+ go func() {
|
|
| 476 |
+ bodyCopyErrc <- cs.writeRequestBody(body, req.Body) |
|
| 477 |
+ }() |
|
| 478 |
+ } else {
|
|
| 479 |
+ if d := cc.responseHeaderTimeout(); d != 0 {
|
|
| 480 |
+ timer := time.NewTimer(d) |
|
| 481 |
+ defer timer.Stop() |
|
| 482 |
+ respHeaderTimer = timer.C |
|
| 483 |
+ } |
|
| 484 |
+ } |
|
| 485 |
+ |
|
| 486 |
+ readLoopResCh := cs.resc |
|
| 487 |
+ requestCanceledCh := requestCancel(req) |
|
| 488 |
+ bodyWritten := false |
|
| 489 |
+ |
|
| 490 |
+ for {
|
|
| 491 |
+ select {
|
|
| 492 |
+ case re := <-readLoopResCh: |
|
| 493 |
+ res := re.res |
|
| 494 |
+ if re.err != nil || res.StatusCode > 299 {
|
|
| 495 |
+ // On error or status code 3xx, 4xx, 5xx, etc abort any |
|
| 496 |
+ // ongoing write, assuming that the server doesn't care |
|
| 497 |
+ // about our request body. If the server replied with 1xx or |
|
| 498 |
+ // 2xx, however, then assume the server DOES potentially |
|
| 499 |
+ // want our body (e.g. full-duplex streaming: |
|
| 500 |
+ // golang.org/issue/13444). If it turns out the server |
|
| 501 |
+ // doesn't, they'll RST_STREAM us soon enough. This is a |
|
| 502 |
+ // heuristic to avoid adding knobs to Transport. Hopefully |
|
| 503 |
+ // we can keep it. |
|
| 504 |
+ cs.abortRequestBodyWrite(errStopReqBodyWrite) |
|
| 505 |
+ } |
|
| 506 |
+ if re.err != nil {
|
|
| 507 |
+ cc.forgetStreamID(cs.ID) |
|
| 508 |
+ return nil, re.err |
|
| 509 |
+ } |
|
| 510 |
+ res.Request = req |
|
| 511 |
+ res.TLS = cc.tlsState |
|
| 512 |
+ return res, nil |
|
| 513 |
+ case <-respHeaderTimer: |
|
| 514 |
+ cc.forgetStreamID(cs.ID) |
|
| 515 |
+ if !hasBody || bodyWritten {
|
|
| 516 |
+ cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) |
|
| 517 |
+ } else {
|
|
| 518 |
+ cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) |
|
| 519 |
+ } |
|
| 520 |
+ return nil, errTimeout |
|
| 521 |
+ case <-requestCanceledCh: |
|
| 522 |
+ cc.forgetStreamID(cs.ID) |
|
| 523 |
+ if !hasBody || bodyWritten {
|
|
| 524 |
+ cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) |
|
| 525 |
+ } else {
|
|
| 526 |
+ cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) |
|
| 527 |
+ } |
|
| 528 |
+ return nil, errRequestCanceled |
|
| 529 |
+ case <-cs.peerReset: |
|
| 530 |
+ // processResetStream already removed the |
|
| 531 |
+ // stream from the streams map; no need for |
|
| 532 |
+ // forgetStreamID. |
|
| 533 |
+ return nil, cs.resetErr |
|
| 534 |
+ case err := <-bodyCopyErrc: |
|
| 535 |
+ if err != nil {
|
|
| 536 |
+ return nil, err |
|
| 537 |
+ } |
|
| 538 |
+ bodyWritten = true |
|
| 539 |
+ if d := cc.responseHeaderTimeout(); d != 0 {
|
|
| 540 |
+ timer := time.NewTimer(d) |
|
| 541 |
+ defer timer.Stop() |
|
| 542 |
+ respHeaderTimer = timer.C |
|
| 543 |
+ } |
|
| 544 |
+ } |
|
| 545 |
+ } |
|
| 546 |
+} |
|
| 547 |
+ |
|
| 548 |
+// requires cc.wmu be held |
|
| 549 |
+func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, hdrs []byte) error {
|
|
| 550 |
+ first := true // first frame written (HEADERS is first, then CONTINUATION) |
|
| 551 |
+ frameSize := int(cc.maxFrameSize) |
|
| 552 |
+ for len(hdrs) > 0 && cc.werr == nil {
|
|
| 313 | 553 |
chunk := hdrs |
| 314 |
- if len(chunk) > int(cc.maxFrameSize) {
|
|
| 315 |
- chunk = chunk[:cc.maxFrameSize] |
|
| 554 |
+ if len(chunk) > frameSize {
|
|
| 555 |
+ chunk = chunk[:frameSize] |
|
| 316 | 556 |
} |
| 317 | 557 |
hdrs = hdrs[len(chunk):] |
| 318 | 558 |
endHeaders := len(hdrs) == 0 |
| 319 | 559 |
if first {
|
| 320 | 560 |
cc.fr.WriteHeaders(HeadersFrameParam{
|
| 321 |
- StreamID: cs.ID, |
|
| 561 |
+ StreamID: streamID, |
|
| 322 | 562 |
BlockFragment: chunk, |
| 323 |
- EndStream: !hasBody, |
|
| 563 |
+ EndStream: endStream, |
|
| 324 | 564 |
EndHeaders: endHeaders, |
| 325 | 565 |
}) |
| 326 | 566 |
first = false |
| 327 | 567 |
} else {
|
| 328 |
- cc.fr.WriteContinuation(cs.ID, endHeaders, chunk) |
|
| 568 |
+ cc.fr.WriteContinuation(streamID, endHeaders, chunk) |
|
| 329 | 569 |
} |
| 330 | 570 |
} |
| 571 |
+ // TODO(bradfitz): this Flush could potentially block (as |
|
| 572 |
+ // could the WriteHeaders call(s) above), which means they |
|
| 573 |
+ // wouldn't respond to Request.Cancel being readable. That's |
|
| 574 |
+ // rare, but this should probably be in a goroutine. |
|
| 331 | 575 |
cc.bw.Flush() |
| 332 |
- werr := cc.werr |
|
| 333 |
- cc.mu.Unlock() |
|
| 576 |
+ return cc.werr |
|
| 577 |
+} |
|
| 334 | 578 |
|
| 335 |
- if hasBody {
|
|
| 336 |
- // TODO: write data. and it should probably be interleaved: |
|
| 337 |
- // go ... io.Copy(dataFrameWriter{cc, cs, ...}, req.Body) ... etc
|
|
| 579 |
+// internal error values; they don't escape to callers |
|
| 580 |
+var ( |
|
| 581 |
+ // abort request body write; don't send cancel |
|
| 582 |
+ errStopReqBodyWrite = errors.New("http2: aborting request body write")
|
|
| 583 |
+ |
|
| 584 |
+ // abort request body write, but send stream reset of cancel. |
|
| 585 |
+ errStopReqBodyWriteAndCancel = errors.New("http2: canceling request")
|
|
| 586 |
+) |
|
| 587 |
+ |
|
| 588 |
+func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (err error) {
|
|
| 589 |
+ cc := cs.cc |
|
| 590 |
+ sentEnd := false // whether we sent the final DATA frame w/ END_STREAM |
|
| 591 |
+ buf := cc.frameScratchBuffer() |
|
| 592 |
+ defer cc.putFrameScratchBuffer(buf) |
|
| 593 |
+ |
|
| 594 |
+ defer func() {
|
|
| 595 |
+ // TODO: write h12Compare test showing whether |
|
| 596 |
+ // Request.Body is closed by the Transport, |
|
| 597 |
+ // and in multiple cases: server replies <=299 and >299 |
|
| 598 |
+ // while still writing request body |
|
| 599 |
+ cerr := bodyCloser.Close() |
|
| 600 |
+ if err == nil {
|
|
| 601 |
+ err = cerr |
|
| 602 |
+ } |
|
| 603 |
+ }() |
|
| 604 |
+ |
|
| 605 |
+ req := cs.req |
|
| 606 |
+ hasTrailers := req.Trailer != nil |
|
| 607 |
+ |
|
| 608 |
+ var sawEOF bool |
|
| 609 |
+ for !sawEOF {
|
|
| 610 |
+ n, err := body.Read(buf) |
|
| 611 |
+ if err == io.EOF {
|
|
| 612 |
+ sawEOF = true |
|
| 613 |
+ err = nil |
|
| 614 |
+ } else if err != nil {
|
|
| 615 |
+ return err |
|
| 616 |
+ } |
|
| 617 |
+ |
|
| 618 |
+ remain := buf[:n] |
|
| 619 |
+ for len(remain) > 0 && err == nil {
|
|
| 620 |
+ var allowed int32 |
|
| 621 |
+ allowed, err = cs.awaitFlowControl(len(remain)) |
|
| 622 |
+ switch {
|
|
| 623 |
+ case err == errStopReqBodyWrite: |
|
| 624 |
+ return err |
|
| 625 |
+ case err == errStopReqBodyWriteAndCancel: |
|
| 626 |
+ cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) |
|
| 627 |
+ return err |
|
| 628 |
+ case err != nil: |
|
| 629 |
+ return err |
|
| 630 |
+ } |
|
| 631 |
+ cc.wmu.Lock() |
|
| 632 |
+ data := remain[:allowed] |
|
| 633 |
+ remain = remain[allowed:] |
|
| 634 |
+ sentEnd = sawEOF && len(remain) == 0 && !hasTrailers |
|
| 635 |
+ err = cc.fr.WriteData(cs.ID, sentEnd, data) |
|
| 636 |
+ if err == nil {
|
|
| 637 |
+ // TODO(bradfitz): this flush is for latency, not bandwidth. |
|
| 638 |
+ // Most requests won't need this. Make this opt-in or opt-out? |
|
| 639 |
+ // Use some heuristic on the body type? Nagel-like timers? |
|
| 640 |
+ // Based on 'n'? Only last chunk of this for loop, unless flow control |
|
| 641 |
+ // tokens are low? For now, always: |
|
| 642 |
+ err = cc.bw.Flush() |
|
| 643 |
+ } |
|
| 644 |
+ cc.wmu.Unlock() |
|
| 645 |
+ } |
|
| 646 |
+ if err != nil {
|
|
| 647 |
+ return err |
|
| 648 |
+ } |
|
| 338 | 649 |
} |
| 339 | 650 |
|
| 340 |
- if werr != nil {
|
|
| 341 |
- return nil, werr |
|
| 651 |
+ cc.wmu.Lock() |
|
| 652 |
+ if !sentEnd {
|
|
| 653 |
+ var trls []byte |
|
| 654 |
+ if hasTrailers {
|
|
| 655 |
+ cc.mu.Lock() |
|
| 656 |
+ trls = cc.encodeTrailers(req) |
|
| 657 |
+ cc.mu.Unlock() |
|
| 658 |
+ } |
|
| 659 |
+ |
|
| 660 |
+ // Avoid forgetting to send an END_STREAM if the encoded |
|
| 661 |
+ // trailers are 0 bytes. Both results produce and END_STREAM. |
|
| 662 |
+ if len(trls) > 0 {
|
|
| 663 |
+ err = cc.writeHeaders(cs.ID, true, trls) |
|
| 664 |
+ } else {
|
|
| 665 |
+ err = cc.fr.WriteData(cs.ID, true, nil) |
|
| 666 |
+ } |
|
| 667 |
+ } |
|
| 668 |
+ if ferr := cc.bw.Flush(); ferr != nil && err == nil {
|
|
| 669 |
+ err = ferr |
|
| 342 | 670 |
} |
| 671 |
+ cc.wmu.Unlock() |
|
| 672 |
+ |
|
| 673 |
+ return err |
|
| 674 |
+} |
|
| 675 |
+ |
|
| 676 |
+// awaitFlowControl waits for [1, min(maxBytes, cc.cs.maxFrameSize)] flow |
|
| 677 |
+// control tokens from the server. |
|
| 678 |
+// It returns either the non-zero number of tokens taken or an error |
|
| 679 |
+// if the stream is dead. |
|
| 680 |
+func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) {
|
|
| 681 |
+ cc := cs.cc |
|
| 682 |
+ cc.mu.Lock() |
|
| 683 |
+ defer cc.mu.Unlock() |
|
| 684 |
+ for {
|
|
| 685 |
+ if cc.closed {
|
|
| 686 |
+ return 0, errClientConnClosed |
|
| 687 |
+ } |
|
| 688 |
+ if cs.stopReqBody != nil {
|
|
| 689 |
+ return 0, cs.stopReqBody |
|
| 690 |
+ } |
|
| 691 |
+ if err := cs.checkReset(); err != nil {
|
|
| 692 |
+ return 0, err |
|
| 693 |
+ } |
|
| 694 |
+ if a := cs.flow.available(); a > 0 {
|
|
| 695 |
+ take := a |
|
| 696 |
+ if int(take) > maxBytes {
|
|
| 343 | 697 |
|
| 344 |
- re := <-cs.resc |
|
| 345 |
- if re.err != nil {
|
|
| 346 |
- return nil, re.err |
|
| 698 |
+ take = int32(maxBytes) // can't truncate int; take is int32 |
|
| 699 |
+ } |
|
| 700 |
+ if take > int32(cc.maxFrameSize) {
|
|
| 701 |
+ take = int32(cc.maxFrameSize) |
|
| 702 |
+ } |
|
| 703 |
+ cs.flow.take(take) |
|
| 704 |
+ return take, nil |
|
| 705 |
+ } |
|
| 706 |
+ cc.cond.Wait() |
|
| 347 | 707 |
} |
| 348 |
- res := re.res |
|
| 349 |
- res.Request = req |
|
| 350 |
- res.TLS = cc.tlsState |
|
| 351 |
- return res, nil |
|
| 352 | 708 |
} |
| 353 | 709 |
|
| 710 |
+type badStringError struct {
|
|
| 711 |
+ what string |
|
| 712 |
+ str string |
|
| 713 |
+} |
|
| 714 |
+ |
|
| 715 |
+func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
|
| 716 |
+ |
|
| 354 | 717 |
// requires cc.mu be held. |
| 355 |
-func (cc *clientConn) encodeHeaders(req *http.Request) []byte {
|
|
| 718 |
+func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) []byte {
|
|
| 356 | 719 |
cc.hbuf.Reset() |
| 357 | 720 |
|
| 358 |
- // TODO(bradfitz): figure out :authority-vs-Host stuff between http2 and Go |
|
| 359 | 721 |
host := req.Host |
| 360 | 722 |
if host == "" {
|
| 361 | 723 |
host = req.URL.Host |
| 362 | 724 |
} |
| 363 | 725 |
|
| 364 |
- path := req.URL.Path |
|
| 365 |
- if path == "" {
|
|
| 366 |
- path = "/" |
|
| 367 |
- } |
|
| 368 |
- |
|
| 369 |
- cc.writeHeader(":authority", host) // probably not right for all sites
|
|
| 726 |
+ // 8.1.2.3 Request Pseudo-Header Fields |
|
| 727 |
+ // The :path pseudo-header field includes the path and query parts of the |
|
| 728 |
+ // target URI (the path-absolute production and optionally a '?' character |
|
| 729 |
+ // followed by the query production (see Sections 3.3 and 3.4 of |
|
| 730 |
+ // [RFC3986]). |
|
| 731 |
+ cc.writeHeader(":authority", host)
|
|
| 370 | 732 |
cc.writeHeader(":method", req.Method)
|
| 371 |
- cc.writeHeader(":path", path)
|
|
| 372 |
- cc.writeHeader(":scheme", "https")
|
|
| 733 |
+ if req.Method != "CONNECT" {
|
|
| 734 |
+ cc.writeHeader(":path", req.URL.RequestURI())
|
|
| 735 |
+ cc.writeHeader(":scheme", "https")
|
|
| 736 |
+ } |
|
| 737 |
+ if trailers != "" {
|
|
| 738 |
+ cc.writeHeader("trailer", trailers)
|
|
| 739 |
+ } |
|
| 373 | 740 |
|
| 741 |
+ var didUA bool |
|
| 374 | 742 |
for k, vv := range req.Header {
|
| 375 | 743 |
lowKey := strings.ToLower(k) |
| 376 |
- if lowKey == "host" {
|
|
| 744 |
+ switch lowKey {
|
|
| 745 |
+ case "host", "content-length": |
|
| 746 |
+ // Host is :authority, already sent. |
|
| 747 |
+ // Content-Length is automatic, set below. |
|
| 377 | 748 |
continue |
| 749 |
+ case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive": |
|
| 750 |
+ // Per 8.1.2.2 Connection-Specific Header |
|
| 751 |
+ // Fields, don't send connection-specific |
|
| 752 |
+ // fields. We have already checked if any |
|
| 753 |
+ // are error-worthy so just ignore the rest. |
|
| 754 |
+ continue |
|
| 755 |
+ case "user-agent": |
|
| 756 |
+ // Match Go's http1 behavior: at most one |
|
| 757 |
+ // User-Agent. If set to nil or empty string, |
|
| 758 |
+ // then omit it. Otherwise if not mentioned, |
|
| 759 |
+ // include the default (below). |
|
| 760 |
+ didUA = true |
|
| 761 |
+ if len(vv) < 1 {
|
|
| 762 |
+ continue |
|
| 763 |
+ } |
|
| 764 |
+ vv = vv[:1] |
|
| 765 |
+ if vv[0] == "" {
|
|
| 766 |
+ continue |
|
| 767 |
+ } |
|
| 378 | 768 |
} |
| 379 | 769 |
for _, v := range vv {
|
| 380 | 770 |
cc.writeHeader(lowKey, v) |
| 381 | 771 |
} |
| 382 | 772 |
} |
| 773 |
+ if shouldSendReqContentLength(req.Method, contentLength) {
|
|
| 774 |
+ cc.writeHeader("content-length", strconv.FormatInt(contentLength, 10))
|
|
| 775 |
+ } |
|
| 776 |
+ if addGzipHeader {
|
|
| 777 |
+ cc.writeHeader("accept-encoding", "gzip")
|
|
| 778 |
+ } |
|
| 779 |
+ if !didUA {
|
|
| 780 |
+ cc.writeHeader("user-agent", defaultUserAgent)
|
|
| 781 |
+ } |
|
| 383 | 782 |
return cc.hbuf.Bytes() |
| 384 | 783 |
} |
| 385 | 784 |
|
| 386 |
-func (cc *clientConn) writeHeader(name, value string) {
|
|
| 387 |
- log.Printf("sending %q = %q", name, value)
|
|
| 785 |
+// shouldSendReqContentLength reports whether the http2.Transport should send |
|
| 786 |
+// a "content-length" request header. This logic is basically a copy of the net/http |
|
| 787 |
+// transferWriter.shouldSendContentLength. |
|
| 788 |
+// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown). |
|
| 789 |
+// -1 means unknown. |
|
| 790 |
+func shouldSendReqContentLength(method string, contentLength int64) bool {
|
|
| 791 |
+ if contentLength > 0 {
|
|
| 792 |
+ return true |
|
| 793 |
+ } |
|
| 794 |
+ if contentLength < 0 {
|
|
| 795 |
+ return false |
|
| 796 |
+ } |
|
| 797 |
+ // For zero bodies, whether we send a content-length depends on the method. |
|
| 798 |
+ // It also kinda doesn't matter for http2 either way, with END_STREAM. |
|
| 799 |
+ switch method {
|
|
| 800 |
+ case "POST", "PUT", "PATCH": |
|
| 801 |
+ return true |
|
| 802 |
+ default: |
|
| 803 |
+ return false |
|
| 804 |
+ } |
|
| 805 |
+} |
|
| 806 |
+ |
|
| 807 |
+// requires cc.mu be held. |
|
| 808 |
+func (cc *ClientConn) encodeTrailers(req *http.Request) []byte {
|
|
| 809 |
+ cc.hbuf.Reset() |
|
| 810 |
+ for k, vv := range req.Trailer {
|
|
| 811 |
+ // Transfer-Encoding, etc.. have already been filter at the |
|
| 812 |
+ // start of RoundTrip |
|
| 813 |
+ lowKey := strings.ToLower(k) |
|
| 814 |
+ for _, v := range vv {
|
|
| 815 |
+ cc.writeHeader(lowKey, v) |
|
| 816 |
+ } |
|
| 817 |
+ } |
|
| 818 |
+ return cc.hbuf.Bytes() |
|
| 819 |
+} |
|
| 820 |
+ |
|
| 821 |
+func (cc *ClientConn) writeHeader(name, value string) {
|
|
| 822 |
+ if VerboseLogs {
|
|
| 823 |
+ log.Printf("http2: Transport encoding header %q = %q", name, value)
|
|
| 824 |
+ } |
|
| 388 | 825 |
cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value})
|
| 389 | 826 |
} |
| 390 | 827 |
|
| ... | ... |
@@ -394,160 +1030,635 @@ type resAndError struct {
|
| 394 | 394 |
} |
| 395 | 395 |
|
| 396 | 396 |
// requires cc.mu be held. |
| 397 |
-func (cc *clientConn) newStream() *clientStream {
|
|
| 397 |
+func (cc *ClientConn) newStream() *clientStream {
|
|
| 398 | 398 |
cs := &clientStream{
|
| 399 |
- ID: cc.nextStreamID, |
|
| 400 |
- resc: make(chan resAndError, 1), |
|
| 399 |
+ cc: cc, |
|
| 400 |
+ ID: cc.nextStreamID, |
|
| 401 |
+ resc: make(chan resAndError, 1), |
|
| 402 |
+ peerReset: make(chan struct{}),
|
|
| 403 |
+ done: make(chan struct{}),
|
|
| 401 | 404 |
} |
| 405 |
+ cs.flow.add(int32(cc.initialWindowSize)) |
|
| 406 |
+ cs.flow.setConnFlow(&cc.flow) |
|
| 407 |
+ cs.inflow.add(transportDefaultStreamFlow) |
|
| 408 |
+ cs.inflow.setConnFlow(&cc.inflow) |
|
| 402 | 409 |
cc.nextStreamID += 2 |
| 403 | 410 |
cc.streams[cs.ID] = cs |
| 404 | 411 |
return cs |
| 405 | 412 |
} |
| 406 | 413 |
|
| 407 |
-func (cc *clientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
|
| 414 |
+func (cc *ClientConn) forgetStreamID(id uint32) {
|
|
| 415 |
+ cc.streamByID(id, true) |
|
| 416 |
+} |
|
| 417 |
+ |
|
| 418 |
+func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
|
| 408 | 419 |
cc.mu.Lock() |
| 409 | 420 |
defer cc.mu.Unlock() |
| 410 | 421 |
cs := cc.streams[id] |
| 411 |
- if andRemove {
|
|
| 422 |
+ if andRemove && cs != nil && !cc.closed {
|
|
| 412 | 423 |
delete(cc.streams, id) |
| 424 |
+ close(cs.done) |
|
| 413 | 425 |
} |
| 414 | 426 |
return cs |
| 415 | 427 |
} |
| 416 | 428 |
|
| 417 |
-// runs in its own goroutine. |
|
| 418 |
-func (cc *clientConn) readLoop() {
|
|
| 419 |
- defer cc.t.removeClientConn(cc) |
|
| 429 |
+// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop. |
|
| 430 |
+type clientConnReadLoop struct {
|
|
| 431 |
+ cc *ClientConn |
|
| 432 |
+ activeRes map[uint32]*clientStream // keyed by streamID |
|
| 433 |
+ closeWhenIdle bool |
|
| 434 |
+} |
|
| 435 |
+ |
|
| 436 |
+// readLoop runs in its own goroutine and reads and dispatches frames. |
|
| 437 |
+func (cc *ClientConn) readLoop() {
|
|
| 438 |
+ rl := &clientConnReadLoop{
|
|
| 439 |
+ cc: cc, |
|
| 440 |
+ activeRes: make(map[uint32]*clientStream), |
|
| 441 |
+ } |
|
| 442 |
+ |
|
| 443 |
+ defer rl.cleanup() |
|
| 444 |
+ cc.readerErr = rl.run() |
|
| 445 |
+ if ce, ok := cc.readerErr.(ConnectionError); ok {
|
|
| 446 |
+ cc.wmu.Lock() |
|
| 447 |
+ cc.fr.WriteGoAway(0, ErrCode(ce), nil) |
|
| 448 |
+ cc.wmu.Unlock() |
|
| 449 |
+ } |
|
| 450 |
+} |
|
| 451 |
+ |
|
| 452 |
+func (rl *clientConnReadLoop) cleanup() {
|
|
| 453 |
+ cc := rl.cc |
|
| 454 |
+ defer cc.tconn.Close() |
|
| 455 |
+ defer cc.t.connPool().MarkDead(cc) |
|
| 420 | 456 |
defer close(cc.readerDone) |
| 421 | 457 |
|
| 422 |
- activeRes := map[uint32]*clientStream{} // keyed by streamID
|
|
| 423 | 458 |
// Close any response bodies if the server closes prematurely. |
| 424 | 459 |
// TODO: also do this if we've written the headers but not |
| 425 | 460 |
// gotten a response yet. |
| 426 |
- defer func() {
|
|
| 427 |
- err := cc.readerErr |
|
| 428 |
- if err == io.EOF {
|
|
| 429 |
- err = io.ErrUnexpectedEOF |
|
| 430 |
- } |
|
| 431 |
- for _, cs := range activeRes {
|
|
| 432 |
- cs.pw.CloseWithError(err) |
|
| 461 |
+ err := cc.readerErr |
|
| 462 |
+ if err == io.EOF {
|
|
| 463 |
+ err = io.ErrUnexpectedEOF |
|
| 464 |
+ } |
|
| 465 |
+ cc.mu.Lock() |
|
| 466 |
+ for _, cs := range rl.activeRes {
|
|
| 467 |
+ cs.bufPipe.CloseWithError(err) |
|
| 468 |
+ } |
|
| 469 |
+ for _, cs := range cc.streams {
|
|
| 470 |
+ select {
|
|
| 471 |
+ case cs.resc <- resAndError{err: err}:
|
|
| 472 |
+ default: |
|
| 433 | 473 |
} |
| 434 |
- }() |
|
| 435 |
- |
|
| 436 |
- // continueStreamID is the stream ID we're waiting for |
|
| 437 |
- // continuation frames for. |
|
| 438 |
- var continueStreamID uint32 |
|
| 474 |
+ close(cs.done) |
|
| 475 |
+ } |
|
| 476 |
+ cc.closed = true |
|
| 477 |
+ cc.cond.Broadcast() |
|
| 478 |
+ cc.mu.Unlock() |
|
| 479 |
+} |
|
| 439 | 480 |
|
| 481 |
+func (rl *clientConnReadLoop) run() error {
|
|
| 482 |
+ cc := rl.cc |
|
| 483 |
+ rl.closeWhenIdle = cc.t.disableKeepAlives() |
|
| 484 |
+ gotReply := false // ever saw a reply |
|
| 440 | 485 |
for {
|
| 441 | 486 |
f, err := cc.fr.ReadFrame() |
| 442 | 487 |
if err != nil {
|
| 443 |
- cc.readerErr = err |
|
| 444 |
- return |
|
| 488 |
+ cc.vlogf("Transport readFrame error: (%T) %v", err, err)
|
|
| 445 | 489 |
} |
| 446 |
- log.Printf("Transport received %v: %#v", f.Header(), f)
|
|
| 447 |
- |
|
| 448 |
- streamID := f.Header().StreamID |
|
| 449 |
- |
|
| 450 |
- _, isContinue := f.(*ContinuationFrame) |
|
| 451 |
- if isContinue {
|
|
| 452 |
- if streamID != continueStreamID {
|
|
| 453 |
- log.Printf("Protocol violation: got CONTINUATION with id %d; want %d", streamID, continueStreamID)
|
|
| 454 |
- cc.readerErr = ConnectionError(ErrCodeProtocol) |
|
| 455 |
- return |
|
| 490 |
+ if se, ok := err.(StreamError); ok {
|
|
| 491 |
+ if cs := cc.streamByID(se.StreamID, true /*ended; remove it*/); cs != nil {
|
|
| 492 |
+ rl.endStreamError(cs, cc.fr.errDetail) |
|
| 456 | 493 |
} |
| 457 |
- } else if continueStreamID != 0 {
|
|
| 458 |
- // Continue frames need to be adjacent in the stream |
|
| 459 |
- // and we were in the middle of headers. |
|
| 460 |
- log.Printf("Protocol violation: got %T for stream %d, want CONTINUATION for %d", f, streamID, continueStreamID)
|
|
| 461 |
- cc.readerErr = ConnectionError(ErrCodeProtocol) |
|
| 462 |
- return |
|
| 463 |
- } |
|
| 464 |
- |
|
| 465 |
- if streamID%2 == 0 {
|
|
| 466 |
- // Ignore streams pushed from the server for now. |
|
| 467 |
- // These always have an even stream id. |
|
| 468 | 494 |
continue |
| 495 |
+ } else if err != nil {
|
|
| 496 |
+ return err |
|
| 469 | 497 |
} |
| 470 |
- streamEnded := false |
|
| 471 |
- if ff, ok := f.(streamEnder); ok {
|
|
| 472 |
- streamEnded = ff.StreamEnded() |
|
| 473 |
- } |
|
| 474 |
- |
|
| 475 |
- cs := cc.streamByID(streamID, streamEnded) |
|
| 476 |
- if cs == nil {
|
|
| 477 |
- log.Printf("Received frame for untracked stream ID %d", streamID)
|
|
| 478 |
- continue |
|
| 498 |
+ if VerboseLogs {
|
|
| 499 |
+ cc.vlogf("http2: Transport received %s", summarizeFrame(f))
|
|
| 479 | 500 |
} |
| 501 |
+ maybeIdle := false // whether frame might transition us to idle |
|
| 480 | 502 |
|
| 481 | 503 |
switch f := f.(type) {
|
| 482 |
- case *HeadersFrame: |
|
| 483 |
- cc.nextRes = &http.Response{
|
|
| 484 |
- Proto: "HTTP/2.0", |
|
| 485 |
- ProtoMajor: 2, |
|
| 486 |
- Header: make(http.Header), |
|
| 487 |
- } |
|
| 488 |
- cs.pr, cs.pw = io.Pipe() |
|
| 489 |
- cc.hdec.Write(f.HeaderBlockFragment()) |
|
| 490 |
- case *ContinuationFrame: |
|
| 491 |
- cc.hdec.Write(f.HeaderBlockFragment()) |
|
| 504 |
+ case *MetaHeadersFrame: |
|
| 505 |
+ err = rl.processHeaders(f) |
|
| 506 |
+ maybeIdle = true |
|
| 507 |
+ gotReply = true |
|
| 492 | 508 |
case *DataFrame: |
| 493 |
- log.Printf("DATA: %q", f.Data())
|
|
| 494 |
- cs.pw.Write(f.Data()) |
|
| 509 |
+ err = rl.processData(f) |
|
| 510 |
+ maybeIdle = true |
|
| 495 | 511 |
case *GoAwayFrame: |
| 496 |
- cc.t.removeClientConn(cc) |
|
| 497 |
- if f.ErrCode != 0 {
|
|
| 498 |
- // TODO: deal with GOAWAY more. particularly the error code |
|
| 499 |
- log.Printf("transport got GOAWAY with error code = %v", f.ErrCode)
|
|
| 500 |
- } |
|
| 501 |
- cc.setGoAway(f) |
|
| 512 |
+ err = rl.processGoAway(f) |
|
| 513 |
+ maybeIdle = true |
|
| 514 |
+ case *RSTStreamFrame: |
|
| 515 |
+ err = rl.processResetStream(f) |
|
| 516 |
+ maybeIdle = true |
|
| 517 |
+ case *SettingsFrame: |
|
| 518 |
+ err = rl.processSettings(f) |
|
| 519 |
+ case *PushPromiseFrame: |
|
| 520 |
+ err = rl.processPushPromise(f) |
|
| 521 |
+ case *WindowUpdateFrame: |
|
| 522 |
+ err = rl.processWindowUpdate(f) |
|
| 523 |
+ case *PingFrame: |
|
| 524 |
+ err = rl.processPing(f) |
|
| 502 | 525 |
default: |
| 503 |
- log.Printf("Transport: unhandled response frame type %T", f)
|
|
| 526 |
+ cc.logf("Transport: unhandled response frame type %T", f)
|
|
| 504 | 527 |
} |
| 505 |
- headersEnded := false |
|
| 506 |
- if he, ok := f.(headersEnder); ok {
|
|
| 507 |
- headersEnded = he.HeadersEnded() |
|
| 508 |
- if headersEnded {
|
|
| 509 |
- continueStreamID = 0 |
|
| 510 |
- } else {
|
|
| 511 |
- continueStreamID = streamID |
|
| 512 |
- } |
|
| 528 |
+ if err != nil {
|
|
| 529 |
+ return err |
|
| 513 | 530 |
} |
| 531 |
+ if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 {
|
|
| 532 |
+ cc.closeIfIdle() |
|
| 533 |
+ } |
|
| 534 |
+ } |
|
| 535 |
+} |
|
| 536 |
+ |
|
| 537 |
+func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
|
| 538 |
+ cc := rl.cc |
|
| 539 |
+ cs := cc.streamByID(f.StreamID, f.StreamEnded()) |
|
| 540 |
+ if cs == nil {
|
|
| 541 |
+ // We'd get here if we canceled a request while the |
|
| 542 |
+ // server had its response still in flight. So if this |
|
| 543 |
+ // was just something we canceled, ignore it. |
|
| 544 |
+ return nil |
|
| 545 |
+ } |
|
| 546 |
+ if !cs.pastHeaders {
|
|
| 547 |
+ cs.pastHeaders = true |
|
| 548 |
+ } else {
|
|
| 549 |
+ return rl.processTrailers(cs, f) |
|
| 550 |
+ } |
|
| 514 | 551 |
|
| 515 |
- if streamEnded {
|
|
| 516 |
- cs.pw.Close() |
|
| 517 |
- delete(activeRes, streamID) |
|
| 552 |
+ res, err := rl.handleResponse(cs, f) |
|
| 553 |
+ if err != nil {
|
|
| 554 |
+ if _, ok := err.(ConnectionError); ok {
|
|
| 555 |
+ return err |
|
| 518 | 556 |
} |
| 519 |
- if headersEnded {
|
|
| 520 |
- if cs == nil {
|
|
| 521 |
- panic("couldn't find stream") // TODO be graceful
|
|
| 557 |
+ // Any other error type is a stream error. |
|
| 558 |
+ cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err) |
|
| 559 |
+ cs.resc <- resAndError{err: err}
|
|
| 560 |
+ return nil // return nil from process* funcs to keep conn alive |
|
| 561 |
+ } |
|
| 562 |
+ if res == nil {
|
|
| 563 |
+ // (nil, nil) special case. See handleResponse docs. |
|
| 564 |
+ return nil |
|
| 565 |
+ } |
|
| 566 |
+ if res.Body != noBody {
|
|
| 567 |
+ rl.activeRes[cs.ID] = cs |
|
| 568 |
+ } |
|
| 569 |
+ cs.resTrailer = &res.Trailer |
|
| 570 |
+ cs.resc <- resAndError{res: res}
|
|
| 571 |
+ return nil |
|
| 572 |
+} |
|
| 573 |
+ |
|
| 574 |
+// may return error types nil, or ConnectionError. Any other error value |
|
| 575 |
+// is a StreamError of type ErrCodeProtocol. The returned error in that case |
|
| 576 |
+// is the detail. |
|
| 577 |
+// |
|
| 578 |
+// As a special case, handleResponse may return (nil, nil) to skip the |
|
| 579 |
+// frame (currently only used for 100 expect continue). This special |
|
| 580 |
+// case is going away after Issue 13851 is fixed. |
|
| 581 |
+func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
|
|
| 582 |
+ if f.Truncated {
|
|
| 583 |
+ return nil, errResponseHeaderListSize |
|
| 584 |
+ } |
|
| 585 |
+ |
|
| 586 |
+ status := f.PseudoValue("status")
|
|
| 587 |
+ if status == "" {
|
|
| 588 |
+ return nil, errors.New("missing status pseudo header")
|
|
| 589 |
+ } |
|
| 590 |
+ statusCode, err := strconv.Atoi(status) |
|
| 591 |
+ if err != nil {
|
|
| 592 |
+ return nil, errors.New("malformed non-numeric status pseudo header")
|
|
| 593 |
+ } |
|
| 594 |
+ |
|
| 595 |
+ if statusCode == 100 {
|
|
| 596 |
+ // Just skip 100-continue response headers for now. |
|
| 597 |
+ // TODO: golang.org/issue/13851 for doing it properly. |
|
| 598 |
+ cs.pastHeaders = false // do it all again |
|
| 599 |
+ return nil, nil |
|
| 600 |
+ } |
|
| 601 |
+ |
|
| 602 |
+ header := make(http.Header) |
|
| 603 |
+ res := &http.Response{
|
|
| 604 |
+ Proto: "HTTP/2.0", |
|
| 605 |
+ ProtoMajor: 2, |
|
| 606 |
+ Header: header, |
|
| 607 |
+ StatusCode: statusCode, |
|
| 608 |
+ Status: status + " " + http.StatusText(statusCode), |
|
| 609 |
+ } |
|
| 610 |
+ for _, hf := range f.RegularFields() {
|
|
| 611 |
+ key := http.CanonicalHeaderKey(hf.Name) |
|
| 612 |
+ if key == "Trailer" {
|
|
| 613 |
+ t := res.Trailer |
|
| 614 |
+ if t == nil {
|
|
| 615 |
+ t = make(http.Header) |
|
| 616 |
+ res.Trailer = t |
|
| 522 | 617 |
} |
| 523 |
- // TODO: set the Body to one which notes the |
|
| 524 |
- // Close and also sends the server a |
|
| 525 |
- // RST_STREAM |
|
| 526 |
- cc.nextRes.Body = cs.pr |
|
| 527 |
- res := cc.nextRes |
|
| 528 |
- activeRes[streamID] = cs |
|
| 529 |
- cs.resc <- resAndError{res: res}
|
|
| 618 |
+ foreachHeaderElement(hf.Value, func(v string) {
|
|
| 619 |
+ t[http.CanonicalHeaderKey(v)] = nil |
|
| 620 |
+ }) |
|
| 621 |
+ } else {
|
|
| 622 |
+ header[key] = append(header[key], hf.Value) |
|
| 530 | 623 |
} |
| 531 | 624 |
} |
| 625 |
+ |
|
| 626 |
+ streamEnded := f.StreamEnded() |
|
| 627 |
+ isHead := cs.req.Method == "HEAD" |
|
| 628 |
+ if !streamEnded || isHead {
|
|
| 629 |
+ res.ContentLength = -1 |
|
| 630 |
+ if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
|
| 631 |
+ if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
|
|
| 632 |
+ res.ContentLength = clen64 |
|
| 633 |
+ } else {
|
|
| 634 |
+ // TODO: care? unlike http/1, it won't mess up our framing, so it's |
|
| 635 |
+ // more safe smuggling-wise to ignore. |
|
| 636 |
+ } |
|
| 637 |
+ } else if len(clens) > 1 {
|
|
| 638 |
+ // TODO: care? unlike http/1, it won't mess up our framing, so it's |
|
| 639 |
+ // more safe smuggling-wise to ignore. |
|
| 640 |
+ } |
|
| 641 |
+ } |
|
| 642 |
+ |
|
| 643 |
+ if streamEnded || isHead {
|
|
| 644 |
+ res.Body = noBody |
|
| 645 |
+ return res, nil |
|
| 646 |
+ } |
|
| 647 |
+ |
|
| 648 |
+ buf := new(bytes.Buffer) // TODO(bradfitz): recycle this garbage |
|
| 649 |
+ cs.bufPipe = pipe{b: buf}
|
|
| 650 |
+ cs.bytesRemain = res.ContentLength |
|
| 651 |
+ res.Body = transportResponseBody{cs}
|
|
| 652 |
+ go cs.awaitRequestCancel(requestCancel(cs.req)) |
|
| 653 |
+ |
|
| 654 |
+ if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
|
|
| 655 |
+ res.Header.Del("Content-Encoding")
|
|
| 656 |
+ res.Header.Del("Content-Length")
|
|
| 657 |
+ res.ContentLength = -1 |
|
| 658 |
+ res.Body = &gzipReader{body: res.Body}
|
|
| 659 |
+ } |
|
| 660 |
+ return res, nil |
|
| 532 | 661 |
} |
| 533 | 662 |
|
| 534 |
-func (cc *clientConn) onNewHeaderField(f hpack.HeaderField) {
|
|
| 535 |
- // TODO: verifiy pseudo headers come before non-pseudo headers |
|
| 536 |
- // TODO: verifiy the status is set |
|
| 537 |
- log.Printf("Header field: %+v", f)
|
|
| 538 |
- if f.Name == ":status" {
|
|
| 539 |
- code, err := strconv.Atoi(f.Value) |
|
| 540 |
- if err != nil {
|
|
| 541 |
- panic("TODO: be graceful")
|
|
| 663 |
+func (rl *clientConnReadLoop) processTrailers(cs *clientStream, f *MetaHeadersFrame) error {
|
|
| 664 |
+ if cs.pastTrailers {
|
|
| 665 |
+ // Too many HEADERS frames for this stream. |
|
| 666 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 667 |
+ } |
|
| 668 |
+ cs.pastTrailers = true |
|
| 669 |
+ if !f.StreamEnded() {
|
|
| 670 |
+ // We expect that any headers for trailers also |
|
| 671 |
+ // has END_STREAM. |
|
| 672 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 673 |
+ } |
|
| 674 |
+ if len(f.PseudoFields()) > 0 {
|
|
| 675 |
+ // No pseudo header fields are defined for trailers. |
|
| 676 |
+ // TODO: ConnectionError might be overly harsh? Check. |
|
| 677 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 678 |
+ } |
|
| 679 |
+ |
|
| 680 |
+ trailer := make(http.Header) |
|
| 681 |
+ for _, hf := range f.RegularFields() {
|
|
| 682 |
+ key := http.CanonicalHeaderKey(hf.Name) |
|
| 683 |
+ trailer[key] = append(trailer[key], hf.Value) |
|
| 684 |
+ } |
|
| 685 |
+ cs.trailer = trailer |
|
| 686 |
+ |
|
| 687 |
+ rl.endStream(cs) |
|
| 688 |
+ return nil |
|
| 689 |
+} |
|
| 690 |
+ |
|
| 691 |
+// transportResponseBody is the concrete type of Transport.RoundTrip's |
|
| 692 |
+// Response.Body. It is an io.ReadCloser. On Read, it reads from cs.body. |
|
| 693 |
+// On Close it sends RST_STREAM if EOF wasn't already seen. |
|
| 694 |
+type transportResponseBody struct {
|
|
| 695 |
+ cs *clientStream |
|
| 696 |
+} |
|
| 697 |
+ |
|
| 698 |
+func (b transportResponseBody) Read(p []byte) (n int, err error) {
|
|
| 699 |
+ cs := b.cs |
|
| 700 |
+ cc := cs.cc |
|
| 701 |
+ |
|
| 702 |
+ if cs.readErr != nil {
|
|
| 703 |
+ return 0, cs.readErr |
|
| 704 |
+ } |
|
| 705 |
+ n, err = b.cs.bufPipe.Read(p) |
|
| 706 |
+ if cs.bytesRemain != -1 {
|
|
| 707 |
+ if int64(n) > cs.bytesRemain {
|
|
| 708 |
+ n = int(cs.bytesRemain) |
|
| 709 |
+ if err == nil {
|
|
| 710 |
+ err = errors.New("net/http: server replied with more than declared Content-Length; truncated")
|
|
| 711 |
+ cc.writeStreamReset(cs.ID, ErrCodeProtocol, err) |
|
| 712 |
+ } |
|
| 713 |
+ cs.readErr = err |
|
| 714 |
+ return int(cs.bytesRemain), err |
|
| 715 |
+ } |
|
| 716 |
+ cs.bytesRemain -= int64(n) |
|
| 717 |
+ if err == io.EOF && cs.bytesRemain > 0 {
|
|
| 718 |
+ err = io.ErrUnexpectedEOF |
|
| 719 |
+ cs.readErr = err |
|
| 720 |
+ return n, err |
|
| 542 | 721 |
} |
| 543 |
- cc.nextRes.Status = f.Value + " " + http.StatusText(code) |
|
| 544 |
- cc.nextRes.StatusCode = code |
|
| 545 |
- return |
|
| 546 | 722 |
} |
| 547 |
- if strings.HasPrefix(f.Name, ":") {
|
|
| 548 |
- // "Endpoints MUST NOT generate pseudo-header fields other than those defined in this document." |
|
| 549 |
- // TODO: treat as invalid? |
|
| 723 |
+ if n == 0 {
|
|
| 724 |
+ // No flow control tokens to send back. |
|
| 550 | 725 |
return |
| 551 | 726 |
} |
| 552 |
- cc.nextRes.Header.Add(http.CanonicalHeaderKey(f.Name), f.Value) |
|
| 727 |
+ |
|
| 728 |
+ cc.mu.Lock() |
|
| 729 |
+ defer cc.mu.Unlock() |
|
| 730 |
+ |
|
| 731 |
+ var connAdd, streamAdd int32 |
|
| 732 |
+ // Check the conn-level first, before the stream-level. |
|
| 733 |
+ if v := cc.inflow.available(); v < transportDefaultConnFlow/2 {
|
|
| 734 |
+ connAdd = transportDefaultConnFlow - v |
|
| 735 |
+ cc.inflow.add(connAdd) |
|
| 736 |
+ } |
|
| 737 |
+ if err == nil { // No need to refresh if the stream is over or failed.
|
|
| 738 |
+ if v := cs.inflow.available(); v < transportDefaultStreamFlow-transportDefaultStreamMinRefresh {
|
|
| 739 |
+ streamAdd = transportDefaultStreamFlow - v |
|
| 740 |
+ cs.inflow.add(streamAdd) |
|
| 741 |
+ } |
|
| 742 |
+ } |
|
| 743 |
+ if connAdd != 0 || streamAdd != 0 {
|
|
| 744 |
+ cc.wmu.Lock() |
|
| 745 |
+ defer cc.wmu.Unlock() |
|
| 746 |
+ if connAdd != 0 {
|
|
| 747 |
+ cc.fr.WriteWindowUpdate(0, mustUint31(connAdd)) |
|
| 748 |
+ } |
|
| 749 |
+ if streamAdd != 0 {
|
|
| 750 |
+ cc.fr.WriteWindowUpdate(cs.ID, mustUint31(streamAdd)) |
|
| 751 |
+ } |
|
| 752 |
+ cc.bw.Flush() |
|
| 753 |
+ } |
|
| 754 |
+ return |
|
| 755 |
+} |
|
| 756 |
+ |
|
| 757 |
+var errClosedResponseBody = errors.New("http2: response body closed")
|
|
| 758 |
+ |
|
| 759 |
+func (b transportResponseBody) Close() error {
|
|
| 760 |
+ cs := b.cs |
|
| 761 |
+ if cs.bufPipe.Err() != io.EOF {
|
|
| 762 |
+ // TODO: write test for this |
|
| 763 |
+ cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) |
|
| 764 |
+ } |
|
| 765 |
+ cs.bufPipe.BreakWithError(errClosedResponseBody) |
|
| 766 |
+ return nil |
|
| 767 |
+} |
|
| 768 |
+ |
|
| 769 |
+func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
|
| 770 |
+ cc := rl.cc |
|
| 771 |
+ cs := cc.streamByID(f.StreamID, f.StreamEnded()) |
|
| 772 |
+ if cs == nil {
|
|
| 773 |
+ cc.mu.Lock() |
|
| 774 |
+ neverSent := cc.nextStreamID |
|
| 775 |
+ cc.mu.Unlock() |
|
| 776 |
+ if f.StreamID >= neverSent {
|
|
| 777 |
+ // We never asked for this. |
|
| 778 |
+ cc.logf("http2: Transport received unsolicited DATA frame; closing connection")
|
|
| 779 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 780 |
+ } |
|
| 781 |
+ // We probably did ask for this, but canceled. Just ignore it. |
|
| 782 |
+ // TODO: be stricter here? only silently ignore things which |
|
| 783 |
+ // we canceled, but not things which were closed normally |
|
| 784 |
+ // by the peer? Tough without accumulating too much state. |
|
| 785 |
+ return nil |
|
| 786 |
+ } |
|
| 787 |
+ if data := f.Data(); len(data) > 0 {
|
|
| 788 |
+ if cs.bufPipe.b == nil {
|
|
| 789 |
+ // Data frame after it's already closed? |
|
| 790 |
+ cc.logf("http2: Transport received DATA frame for closed stream; closing connection")
|
|
| 791 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 792 |
+ } |
|
| 793 |
+ |
|
| 794 |
+ // Check connection-level flow control. |
|
| 795 |
+ cc.mu.Lock() |
|
| 796 |
+ if cs.inflow.available() >= int32(len(data)) {
|
|
| 797 |
+ cs.inflow.take(int32(len(data))) |
|
| 798 |
+ } else {
|
|
| 799 |
+ cc.mu.Unlock() |
|
| 800 |
+ return ConnectionError(ErrCodeFlowControl) |
|
| 801 |
+ } |
|
| 802 |
+ cc.mu.Unlock() |
|
| 803 |
+ |
|
| 804 |
+ if _, err := cs.bufPipe.Write(data); err != nil {
|
|
| 805 |
+ rl.endStreamError(cs, err) |
|
| 806 |
+ return err |
|
| 807 |
+ } |
|
| 808 |
+ } |
|
| 809 |
+ |
|
| 810 |
+ if f.StreamEnded() {
|
|
| 811 |
+ rl.endStream(cs) |
|
| 812 |
+ } |
|
| 813 |
+ return nil |
|
| 814 |
+} |
|
| 815 |
+ |
|
| 816 |
+var errInvalidTrailers = errors.New("http2: invalid trailers")
|
|
| 817 |
+ |
|
| 818 |
+func (rl *clientConnReadLoop) endStream(cs *clientStream) {
|
|
| 819 |
+ // TODO: check that any declared content-length matches, like |
|
| 820 |
+ // server.go's (*stream).endStream method. |
|
| 821 |
+ rl.endStreamError(cs, nil) |
|
| 822 |
+} |
|
| 823 |
+ |
|
| 824 |
+func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
|
|
| 825 |
+ var code func() |
|
| 826 |
+ if err == nil {
|
|
| 827 |
+ err = io.EOF |
|
| 828 |
+ code = cs.copyTrailers |
|
| 829 |
+ } |
|
| 830 |
+ cs.bufPipe.closeWithErrorAndCode(err, code) |
|
| 831 |
+ delete(rl.activeRes, cs.ID) |
|
| 832 |
+ if cs.req.Close || cs.req.Header.Get("Connection") == "close" {
|
|
| 833 |
+ rl.closeWhenIdle = true |
|
| 834 |
+ } |
|
| 835 |
+} |
|
| 836 |
+ |
|
| 837 |
+func (cs *clientStream) copyTrailers() {
|
|
| 838 |
+ for k, vv := range cs.trailer {
|
|
| 839 |
+ t := cs.resTrailer |
|
| 840 |
+ if *t == nil {
|
|
| 841 |
+ *t = make(http.Header) |
|
| 842 |
+ } |
|
| 843 |
+ (*t)[k] = vv |
|
| 844 |
+ } |
|
| 845 |
+} |
|
| 846 |
+ |
|
| 847 |
+func (rl *clientConnReadLoop) processGoAway(f *GoAwayFrame) error {
|
|
| 848 |
+ cc := rl.cc |
|
| 849 |
+ cc.t.connPool().MarkDead(cc) |
|
| 850 |
+ if f.ErrCode != 0 {
|
|
| 851 |
+ // TODO: deal with GOAWAY more. particularly the error code |
|
| 852 |
+ cc.vlogf("transport got GOAWAY with error code = %v", f.ErrCode)
|
|
| 853 |
+ } |
|
| 854 |
+ cc.setGoAway(f) |
|
| 855 |
+ return nil |
|
| 856 |
+} |
|
| 857 |
+ |
|
| 858 |
+func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
|
|
| 859 |
+ cc := rl.cc |
|
| 860 |
+ cc.mu.Lock() |
|
| 861 |
+ defer cc.mu.Unlock() |
|
| 862 |
+ return f.ForeachSetting(func(s Setting) error {
|
|
| 863 |
+ switch s.ID {
|
|
| 864 |
+ case SettingMaxFrameSize: |
|
| 865 |
+ cc.maxFrameSize = s.Val |
|
| 866 |
+ case SettingMaxConcurrentStreams: |
|
| 867 |
+ cc.maxConcurrentStreams = s.Val |
|
| 868 |
+ case SettingInitialWindowSize: |
|
| 869 |
+ // TODO: error if this is too large. |
|
| 870 |
+ |
|
| 871 |
+ // TODO: adjust flow control of still-open |
|
| 872 |
+ // frames by the difference of the old initial |
|
| 873 |
+ // window size and this one. |
|
| 874 |
+ cc.initialWindowSize = s.Val |
|
| 875 |
+ default: |
|
| 876 |
+ // TODO(bradfitz): handle more settings? SETTINGS_HEADER_TABLE_SIZE probably. |
|
| 877 |
+ cc.vlogf("Unhandled Setting: %v", s)
|
|
| 878 |
+ } |
|
| 879 |
+ return nil |
|
| 880 |
+ }) |
|
| 881 |
+} |
|
| 882 |
+ |
|
| 883 |
+func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
|
|
| 884 |
+ cc := rl.cc |
|
| 885 |
+ cs := cc.streamByID(f.StreamID, false) |
|
| 886 |
+ if f.StreamID != 0 && cs == nil {
|
|
| 887 |
+ return nil |
|
| 888 |
+ } |
|
| 889 |
+ |
|
| 890 |
+ cc.mu.Lock() |
|
| 891 |
+ defer cc.mu.Unlock() |
|
| 892 |
+ |
|
| 893 |
+ fl := &cc.flow |
|
| 894 |
+ if cs != nil {
|
|
| 895 |
+ fl = &cs.flow |
|
| 896 |
+ } |
|
| 897 |
+ if !fl.add(int32(f.Increment)) {
|
|
| 898 |
+ return ConnectionError(ErrCodeFlowControl) |
|
| 899 |
+ } |
|
| 900 |
+ cc.cond.Broadcast() |
|
| 901 |
+ return nil |
|
| 902 |
+} |
|
| 903 |
+ |
|
| 904 |
+func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
|
|
| 905 |
+ cs := rl.cc.streamByID(f.StreamID, true) |
|
| 906 |
+ if cs == nil {
|
|
| 907 |
+ // TODO: return error if server tries to RST_STEAM an idle stream |
|
| 908 |
+ return nil |
|
| 909 |
+ } |
|
| 910 |
+ select {
|
|
| 911 |
+ case <-cs.peerReset: |
|
| 912 |
+ // Already reset. |
|
| 913 |
+ // This is the only goroutine |
|
| 914 |
+ // which closes this, so there |
|
| 915 |
+ // isn't a race. |
|
| 916 |
+ default: |
|
| 917 |
+ err := StreamError{cs.ID, f.ErrCode}
|
|
| 918 |
+ cs.resetErr = err |
|
| 919 |
+ close(cs.peerReset) |
|
| 920 |
+ cs.bufPipe.CloseWithError(err) |
|
| 921 |
+ cs.cc.cond.Broadcast() // wake up checkReset via clientStream.awaitFlowControl |
|
| 922 |
+ } |
|
| 923 |
+ delete(rl.activeRes, cs.ID) |
|
| 924 |
+ return nil |
|
| 925 |
+} |
|
| 926 |
+ |
|
| 927 |
+func (rl *clientConnReadLoop) processPing(f *PingFrame) error {
|
|
| 928 |
+ if f.IsAck() {
|
|
| 929 |
+ // 6.7 PING: " An endpoint MUST NOT respond to PING frames |
|
| 930 |
+ // containing this flag." |
|
| 931 |
+ return nil |
|
| 932 |
+ } |
|
| 933 |
+ cc := rl.cc |
|
| 934 |
+ cc.wmu.Lock() |
|
| 935 |
+ defer cc.wmu.Unlock() |
|
| 936 |
+ if err := cc.fr.WritePing(true, f.Data); err != nil {
|
|
| 937 |
+ return err |
|
| 938 |
+ } |
|
| 939 |
+ return cc.bw.Flush() |
|
| 940 |
+} |
|
| 941 |
+ |
|
| 942 |
+func (rl *clientConnReadLoop) processPushPromise(f *PushPromiseFrame) error {
|
|
| 943 |
+ // We told the peer we don't want them. |
|
| 944 |
+ // Spec says: |
|
| 945 |
+ // "PUSH_PROMISE MUST NOT be sent if the SETTINGS_ENABLE_PUSH |
|
| 946 |
+ // setting of the peer endpoint is set to 0. An endpoint that |
|
| 947 |
+ // has set this setting and has received acknowledgement MUST |
|
| 948 |
+ // treat the receipt of a PUSH_PROMISE frame as a connection |
|
| 949 |
+ // error (Section 5.4.1) of type PROTOCOL_ERROR." |
|
| 950 |
+ return ConnectionError(ErrCodeProtocol) |
|
| 951 |
+} |
|
| 952 |
+ |
|
| 953 |
+func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error) {
|
|
| 954 |
+ // TODO: do something with err? send it as a debug frame to the peer? |
|
| 955 |
+ // But that's only in GOAWAY. Invent a new frame type? Is there one already? |
|
| 956 |
+ cc.wmu.Lock() |
|
| 957 |
+ cc.fr.WriteRSTStream(streamID, code) |
|
| 958 |
+ cc.bw.Flush() |
|
| 959 |
+ cc.wmu.Unlock() |
|
| 960 |
+} |
|
| 961 |
+ |
|
| 962 |
+var ( |
|
| 963 |
+ errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
|
|
| 964 |
+ errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers")
|
|
| 965 |
+) |
|
| 966 |
+ |
|
| 967 |
+func (cc *ClientConn) logf(format string, args ...interface{}) {
|
|
| 968 |
+ cc.t.logf(format, args...) |
|
| 969 |
+} |
|
| 970 |
+ |
|
| 971 |
+func (cc *ClientConn) vlogf(format string, args ...interface{}) {
|
|
| 972 |
+ cc.t.vlogf(format, args...) |
|
| 973 |
+} |
|
| 974 |
+ |
|
| 975 |
+func (t *Transport) vlogf(format string, args ...interface{}) {
|
|
| 976 |
+ if VerboseLogs {
|
|
| 977 |
+ t.logf(format, args...) |
|
| 978 |
+ } |
|
| 979 |
+} |
|
| 980 |
+ |
|
| 981 |
+func (t *Transport) logf(format string, args ...interface{}) {
|
|
| 982 |
+ log.Printf(format, args...) |
|
| 553 | 983 |
} |
| 984 |
+ |
|
| 985 |
+var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil)) |
|
| 986 |
+ |
|
| 987 |
+func strSliceContains(ss []string, s string) bool {
|
|
| 988 |
+ for _, v := range ss {
|
|
| 989 |
+ if v == s {
|
|
| 990 |
+ return true |
|
| 991 |
+ } |
|
| 992 |
+ } |
|
| 993 |
+ return false |
|
| 994 |
+} |
|
| 995 |
+ |
|
| 996 |
+type erringRoundTripper struct{ err error }
|
|
| 997 |
+ |
|
| 998 |
+func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
|
| 999 |
+ |
|
| 1000 |
+// gzipReader wraps a response body so it can lazily |
|
| 1001 |
+// call gzip.NewReader on the first call to Read |
|
| 1002 |
+type gzipReader struct {
|
|
| 1003 |
+ body io.ReadCloser // underlying Response.Body |
|
| 1004 |
+ zr *gzip.Reader // lazily-initialized gzip reader |
|
| 1005 |
+ zerr error // sticky error |
|
| 1006 |
+} |
|
| 1007 |
+ |
|
| 1008 |
+func (gz *gzipReader) Read(p []byte) (n int, err error) {
|
|
| 1009 |
+ if gz.zerr != nil {
|
|
| 1010 |
+ return 0, gz.zerr |
|
| 1011 |
+ } |
|
| 1012 |
+ if gz.zr == nil {
|
|
| 1013 |
+ gz.zr, err = gzip.NewReader(gz.body) |
|
| 1014 |
+ if err != nil {
|
|
| 1015 |
+ gz.zerr = err |
|
| 1016 |
+ return 0, err |
|
| 1017 |
+ } |
|
| 1018 |
+ } |
|
| 1019 |
+ return gz.zr.Read(p) |
|
| 1020 |
+} |
|
| 1021 |
+ |
|
| 1022 |
+func (gz *gzipReader) Close() error {
|
|
| 1023 |
+ return gz.body.Close() |
|
| 1024 |
+} |
|
| 1025 |
+ |
|
| 1026 |
+type errorReader struct{ err error }
|
|
| 1027 |
+ |
|
| 1028 |
+func (r errorReader) Read(p []byte) (int, error) { return 0, r.err }
|
| ... | ... |
@@ -1,15 +1,13 @@ |
| 1 | 1 |
// Copyright 2014 The Go Authors. All rights reserved. |
| 2 | 2 |
// Use of this source code is governed by a BSD-style |
| 3 | 3 |
// license that can be found in the LICENSE file. |
| 4 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 5 |
-// Licensed under the same terms as Go itself: |
|
| 6 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 7 | 4 |
|
| 8 | 5 |
package http2 |
| 9 | 6 |
|
| 10 | 7 |
import ( |
| 11 | 8 |
"bytes" |
| 12 | 9 |
"fmt" |
| 10 |
+ "log" |
|
| 13 | 11 |
"net/http" |
| 14 | 12 |
"time" |
| 15 | 13 |
|
| ... | ... |
@@ -26,7 +24,11 @@ type writeFramer interface {
|
| 26 | 26 |
// frame writing scheduler (see writeScheduler in writesched.go). |
| 27 | 27 |
// |
| 28 | 28 |
// This interface is implemented by *serverConn. |
| 29 |
-// TODO: use it from the client code too, once it exists. |
|
| 29 |
+// |
|
| 30 |
+// TODO: decide whether to a) use this in the client code (which didn't |
|
| 31 |
+// end up using this yet, because it has a simpler design, not |
|
| 32 |
+// currently implementing priorities), or b) delete this and |
|
| 33 |
+// make the server code a bit more concrete. |
|
| 30 | 34 |
type writeContext interface {
|
| 31 | 35 |
Framer() *Framer |
| 32 | 36 |
Flush() error |
| ... | ... |
@@ -44,6 +46,11 @@ func endsStream(w writeFramer) bool {
|
| 44 | 44 |
return v.endStream |
| 45 | 45 |
case *writeResHeaders: |
| 46 | 46 |
return v.endStream |
| 47 |
+ case nil: |
|
| 48 |
+ // This can only happen if the caller reuses w after it's |
|
| 49 |
+ // been intentionally nil'ed out to prevent use. Keep this |
|
| 50 |
+ // here to catch future refactoring breaking it. |
|
| 51 |
+ panic("endsStream called on nil writeFramer")
|
|
| 47 | 52 |
} |
| 48 | 53 |
return false |
| 49 | 54 |
} |
| ... | ... |
@@ -89,6 +96,16 @@ func (w *writeData) writeFrame(ctx writeContext) error {
|
| 89 | 89 |
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) |
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 |
+// handlerPanicRST is the message sent from handler goroutines when |
|
| 93 |
+// the handler panics. |
|
| 94 |
+type handlerPanicRST struct {
|
|
| 95 |
+ StreamID uint32 |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
|
|
| 99 |
+ return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal) |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 92 | 102 |
func (se StreamError) writeFrame(ctx writeContext) error {
|
| 93 | 103 |
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) |
| 94 | 104 |
} |
| ... | ... |
@@ -106,40 +123,48 @@ func (writeSettingsAck) writeFrame(ctx writeContext) error {
|
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 | 108 |
// writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames |
| 109 |
-// for HTTP response headers from a server handler. |
|
| 109 |
+// for HTTP response headers or trailers from a server handler. |
|
| 110 | 110 |
type writeResHeaders struct {
|
| 111 | 111 |
streamID uint32 |
| 112 |
- httpResCode int |
|
| 112 |
+ httpResCode int // 0 means no ":status" line |
|
| 113 | 113 |
h http.Header // may be nil |
| 114 |
+ trailers []string // if non-nil, which keys of h to write. nil means all. |
|
| 114 | 115 |
endStream bool |
| 115 | 116 |
|
| 117 |
+ date string |
|
| 116 | 118 |
contentType string |
| 117 | 119 |
contentLength string |
| 118 | 120 |
} |
| 119 | 121 |
|
| 122 |
+func encKV(enc *hpack.Encoder, k, v string) {
|
|
| 123 |
+ if VerboseLogs {
|
|
| 124 |
+ log.Printf("http2: server encoding header %q = %q", k, v)
|
|
| 125 |
+ } |
|
| 126 |
+ enc.WriteField(hpack.HeaderField{Name: k, Value: v})
|
|
| 127 |
+} |
|
| 128 |
+ |
|
| 120 | 129 |
func (w *writeResHeaders) writeFrame(ctx writeContext) error {
|
| 121 | 130 |
enc, buf := ctx.HeaderEncoder() |
| 122 | 131 |
buf.Reset() |
| 123 |
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(w.httpResCode)})
|
|
| 124 |
- for k, vv := range w.h {
|
|
| 125 |
- k = lowerHeader(k) |
|
| 126 |
- for _, v := range vv {
|
|
| 127 |
- // TODO: more of "8.1.2.2 Connection-Specific Header Fields" |
|
| 128 |
- if k == "transfer-encoding" && v != "trailers" {
|
|
| 129 |
- continue |
|
| 130 |
- } |
|
| 131 |
- enc.WriteField(hpack.HeaderField{Name: k, Value: v})
|
|
| 132 |
- } |
|
| 132 |
+ |
|
| 133 |
+ if w.httpResCode != 0 {
|
|
| 134 |
+ encKV(enc, ":status", httpCodeString(w.httpResCode)) |
|
| 133 | 135 |
} |
| 136 |
+ |
|
| 137 |
+ encodeHeaders(enc, w.h, w.trailers) |
|
| 138 |
+ |
|
| 134 | 139 |
if w.contentType != "" {
|
| 135 |
- enc.WriteField(hpack.HeaderField{Name: "content-type", Value: w.contentType})
|
|
| 140 |
+ encKV(enc, "content-type", w.contentType) |
|
| 136 | 141 |
} |
| 137 | 142 |
if w.contentLength != "" {
|
| 138 |
- enc.WriteField(hpack.HeaderField{Name: "content-length", Value: w.contentLength})
|
|
| 143 |
+ encKV(enc, "content-length", w.contentLength) |
|
| 144 |
+ } |
|
| 145 |
+ if w.date != "" {
|
|
| 146 |
+ encKV(enc, "date", w.date) |
|
| 139 | 147 |
} |
| 140 | 148 |
|
| 141 | 149 |
headerBlock := buf.Bytes() |
| 142 |
- if len(headerBlock) == 0 {
|
|
| 150 |
+ if len(headerBlock) == 0 && w.trailers == nil {
|
|
| 143 | 151 |
panic("unexpected empty hpack")
|
| 144 | 152 |
} |
| 145 | 153 |
|
| ... | ... |
@@ -185,7 +210,7 @@ type write100ContinueHeadersFrame struct {
|
| 185 | 185 |
func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
|
| 186 | 186 |
enc, buf := ctx.HeaderEncoder() |
| 187 | 187 |
buf.Reset() |
| 188 |
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
|
|
| 188 |
+ encKV(enc, ":status", "100") |
|
| 189 | 189 |
return ctx.Framer().WriteHeaders(HeadersFrameParam{
|
| 190 | 190 |
StreamID: w.streamID, |
| 191 | 191 |
BlockFragment: buf.Bytes(), |
| ... | ... |
@@ -202,3 +227,36 @@ type writeWindowUpdate struct {
|
| 202 | 202 |
func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
|
| 203 | 203 |
return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n) |
| 204 | 204 |
} |
| 205 |
+ |
|
| 206 |
+func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
|
| 207 |
+ if keys == nil {
|
|
| 208 |
+ sorter := sorterPool.Get().(*sorter) |
|
| 209 |
+ // Using defer here, since the returned keys from the |
|
| 210 |
+ // sorter.Keys method is only valid until the sorter |
|
| 211 |
+ // is returned: |
|
| 212 |
+ defer sorterPool.Put(sorter) |
|
| 213 |
+ keys = sorter.Keys(h) |
|
| 214 |
+ } |
|
| 215 |
+ for _, k := range keys {
|
|
| 216 |
+ vv := h[k] |
|
| 217 |
+ k = lowerHeader(k) |
|
| 218 |
+ if !validHeaderFieldName(k) {
|
|
| 219 |
+ // TODO: return an error? golang.org/issue/14048 |
|
| 220 |
+ // For now just omit it. |
|
| 221 |
+ continue |
|
| 222 |
+ } |
|
| 223 |
+ isTE := k == "transfer-encoding" |
|
| 224 |
+ for _, v := range vv {
|
|
| 225 |
+ if !validHeaderFieldValue(v) {
|
|
| 226 |
+ // TODO: return an error? golang.org/issue/14048 |
|
| 227 |
+ // For now just omit it. |
|
| 228 |
+ continue |
|
| 229 |
+ } |
|
| 230 |
+ // TODO: more of "8.1.2.2 Connection-Specific Header Fields" |
|
| 231 |
+ if isTE && v != "trailers" {
|
|
| 232 |
+ continue |
|
| 233 |
+ } |
|
| 234 |
+ encKV(enc, k, v) |
|
| 235 |
+ } |
|
| 236 |
+ } |
|
| 237 |
+} |
| ... | ... |
@@ -1,9 +1,6 @@ |
| 1 | 1 |
// Copyright 2014 The Go Authors. All rights reserved. |
| 2 | 2 |
// Use of this source code is governed by a BSD-style |
| 3 | 3 |
// license that can be found in the LICENSE file. |
| 4 |
-// See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
|
| 5 |
-// Licensed under the same terms as Go itself: |
|
| 6 |
-// https://code.google.com/p/go/source/browse/LICENSE |
|
| 7 | 4 |
|
| 8 | 5 |
package http2 |
| 9 | 6 |
|
| ... | ... |
@@ -95,11 +95,14 @@ var DebugUseAfterFinish = false |
| 95 | 95 |
// |
| 96 | 96 |
// The default AuthRequest function returns (true, true) iff the request comes from localhost/127.0.0.1/[::1]. |
| 97 | 97 |
var AuthRequest = func(req *http.Request) (any, sensitive bool) {
|
| 98 |
+ // RemoteAddr is commonly in the form "IP" or "IP:port". |
|
| 99 |
+ // If it is in the form "IP:port", split off the port. |
|
| 98 | 100 |
host, _, err := net.SplitHostPort(req.RemoteAddr) |
| 99 |
- switch {
|
|
| 100 |
- case err != nil: // Badly formed address; fail closed. |
|
| 101 |
- return false, false |
|
| 102 |
- case host == "localhost" || host == "127.0.0.1" || host == "::1": |
|
| 101 |
+ if err != nil {
|
|
| 102 |
+ host = req.RemoteAddr |
|
| 103 |
+ } |
|
| 104 |
+ switch host {
|
|
| 105 |
+ case "localhost", "127.0.0.1", "::1": |
|
| 103 | 106 |
return true, true |
| 104 | 107 |
default: |
| 105 | 108 |
return false, false |
| ... | ... |
@@ -113,6 +116,7 @@ func init() {
|
| 113 | 113 |
http.Error(w, "not allowed", http.StatusUnauthorized) |
| 114 | 114 |
return |
| 115 | 115 |
} |
| 116 |
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
| 116 | 117 |
Render(w, req, sensitive) |
| 117 | 118 |
}) |
| 118 | 119 |
http.HandleFunc("/debug/events", func(w http.ResponseWriter, req *http.Request) {
|
| ... | ... |
@@ -121,6 +125,7 @@ func init() {
|
| 121 | 121 |
http.Error(w, "not allowed", http.StatusUnauthorized) |
| 122 | 122 |
return |
| 123 | 123 |
} |
| 124 |
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
| 124 | 125 |
RenderEvents(w, req, sensitive) |
| 125 | 126 |
}) |
| 126 | 127 |
} |
| ... | ... |
@@ -172,7 +177,7 @@ func Render(w io.Writer, req *http.Request, sensitive bool) {
|
| 172 | 172 |
|
| 173 | 173 |
completedMu.RLock() |
| 174 | 174 |
data.Families = make([]string, 0, len(completedTraces)) |
| 175 |
- for fam, _ := range completedTraces {
|
|
| 175 |
+ for fam := range completedTraces {
|
|
| 176 | 176 |
data.Families = append(data.Families, fam) |
| 177 | 177 |
} |
| 178 | 178 |
completedMu.RUnlock() |
| ... | ... |
@@ -1,16 +1,39 @@ |
| 1 | 1 |
# How to contribute |
| 2 | 2 |
|
| 3 |
-We definitely welcome patches and contribution to grpc! Here is some guideline |
|
| 3 |
+We definitely welcome patches and contribution to grpc! Here are some guidelines |
|
| 4 | 4 |
and information about how to do so. |
| 5 | 5 |
|
| 6 |
-## Getting started |
|
| 6 |
+## Sending patches |
|
| 7 | 7 |
|
| 8 |
-### Legal requirements |
|
| 8 |
+### Getting started |
|
| 9 |
+ |
|
| 10 |
+1. Check out the code: |
|
| 11 |
+ |
|
| 12 |
+ $ go get google.golang.org/grpc |
|
| 13 |
+ $ cd $GOPATH/src/google.golang.org/grpc |
|
| 14 |
+ |
|
| 15 |
+1. Create a fork of the grpc-go repository. |
|
| 16 |
+1. Add your fork as a remote: |
|
| 17 |
+ |
|
| 18 |
+ $ git remote add fork git@github.com:$YOURGITHUBUSERNAME/grpc-go.git |
|
| 19 |
+ |
|
| 20 |
+1. Make changes, commit them. |
|
| 21 |
+1. Run the test suite: |
|
| 22 |
+ |
|
| 23 |
+ $ make test |
|
| 24 |
+ |
|
| 25 |
+1. Push your changes to your fork: |
|
| 26 |
+ |
|
| 27 |
+ $ git push fork ... |
|
| 28 |
+ |
|
| 29 |
+1. Open a pull request. |
|
| 30 |
+ |
|
| 31 |
+## Legal requirements |
|
| 9 | 32 |
|
| 10 | 33 |
In order to protect both you and ourselves, you will need to sign the |
| 11 | 34 |
[Contributor License Agreement](https://cla.developers.google.com/clas). |
| 12 | 35 |
|
| 13 |
-### Filing Issues |
|
| 36 |
+## Filing Issues |
|
| 14 | 37 |
When filing an issue, make sure to answer these five questions: |
| 15 | 38 |
|
| 16 | 39 |
1. What version of Go are you using (`go version`)? |
| ... | ... |
@@ -1,15 +1,3 @@ |
| 1 |
-.PHONY: \ |
|
| 2 |
- all \ |
|
| 3 |
- deps \ |
|
| 4 |
- updatedeps \ |
|
| 5 |
- testdeps \ |
|
| 6 |
- updatetestdeps \ |
|
| 7 |
- build \ |
|
| 8 |
- proto \ |
|
| 9 |
- test \ |
|
| 10 |
- testrace \ |
|
| 11 |
- clean \ |
|
| 12 |
- |
|
| 13 | 1 |
all: test testrace |
| 14 | 2 |
|
| 15 | 3 |
deps: |
| ... | ... |
@@ -32,7 +20,7 @@ proto: |
| 32 | 32 |
echo "error: protoc not installed" >&2; \ |
| 33 | 33 |
exit 1; \ |
| 34 | 34 |
fi |
| 35 |
- go get -v github.com/golang/protobuf/protoc-gen-go |
|
| 35 |
+ go get -u -v github.com/golang/protobuf/protoc-gen-go |
|
| 36 | 36 |
for file in $$(git ls-files '*.proto'); do \ |
| 37 | 37 |
protoc -I $$(dirname $$file) --go_out=plugins=grpc:$$(dirname $$file) $$file; \ |
| 38 | 38 |
done |
| ... | ... |
@@ -44,7 +32,20 @@ testrace: testdeps |
| 44 | 44 |
go test -v -race -cpu 1,4 google.golang.org/grpc/... |
| 45 | 45 |
|
| 46 | 46 |
clean: |
| 47 |
- go clean google.golang.org/grpc/... |
|
| 47 |
+ go clean -i google.golang.org/grpc/... |
|
| 48 | 48 |
|
| 49 | 49 |
coverage: testdeps |
| 50 | 50 |
./coverage.sh --coveralls |
| 51 |
+ |
|
| 52 |
+.PHONY: \ |
|
| 53 |
+ all \ |
|
| 54 |
+ deps \ |
|
| 55 |
+ updatedeps \ |
|
| 56 |
+ testdeps \ |
|
| 57 |
+ updatetestdeps \ |
|
| 58 |
+ build \ |
|
| 59 |
+ proto \ |
|
| 60 |
+ test \ |
|
| 61 |
+ testrace \ |
|
| 62 |
+ clean \ |
|
| 63 |
+ coverage |
| ... | ... |
@@ -7,7 +7,7 @@ The Go implementation of [gRPC](http://www.grpc.io/): A high performance, open s |
| 7 | 7 |
Installation |
| 8 | 8 |
------------ |
| 9 | 9 |
|
| 10 |
-To install this package, you need to install Go 1.4 or above and setup your Go workspace on your computer. The simplest way to install the library is to run: |
|
| 10 |
+To install this package, you need to install Go and setup your Go workspace on your computer. The simplest way to install the library is to run: |
|
| 11 | 11 |
|
| 12 | 12 |
``` |
| 13 | 13 |
$ go get google.golang.org/grpc |
| ... | ... |
@@ -16,7 +16,7 @@ $ go get google.golang.org/grpc |
| 16 | 16 |
Prerequisites |
| 17 | 17 |
------------- |
| 18 | 18 |
|
| 19 |
-This requires Go 1.4 or above. |
|
| 19 |
+This requires Go 1.5 or later . |
|
| 20 | 20 |
|
| 21 | 21 |
Constraints |
| 22 | 22 |
----------- |
| 23 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,80 @@ |
| 0 |
+package grpc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "math/rand" |
|
| 4 |
+ "time" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// DefaultBackoffConfig uses values specified for backoff in |
|
| 8 |
+// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. |
|
| 9 |
+var ( |
|
| 10 |
+ DefaultBackoffConfig = BackoffConfig{
|
|
| 11 |
+ MaxDelay: 120 * time.Second, |
|
| 12 |
+ baseDelay: 1.0 * time.Second, |
|
| 13 |
+ factor: 1.6, |
|
| 14 |
+ jitter: 0.2, |
|
| 15 |
+ } |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+// backoffStrategy defines the methodology for backing off after a grpc |
|
| 19 |
+// connection failure. |
|
| 20 |
+// |
|
| 21 |
+// This is unexported until the GRPC project decides whether or not to allow |
|
| 22 |
+// alternative backoff strategies. Once a decision is made, this type and its |
|
| 23 |
+// method may be exported. |
|
| 24 |
+type backoffStrategy interface {
|
|
| 25 |
+ // backoff returns the amount of time to wait before the next retry given |
|
| 26 |
+ // the number of consecutive failures. |
|
| 27 |
+ backoff(retries int) time.Duration |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// BackoffConfig defines the parameters for the default GRPC backoff strategy. |
|
| 31 |
+type BackoffConfig struct {
|
|
| 32 |
+ // MaxDelay is the upper bound of backoff delay. |
|
| 33 |
+ MaxDelay time.Duration |
|
| 34 |
+ |
|
| 35 |
+ // TODO(stevvooe): The following fields are not exported, as allowing |
|
| 36 |
+ // changes would violate the current GRPC specification for backoff. If |
|
| 37 |
+ // GRPC decides to allow more interesting backoff strategies, these fields |
|
| 38 |
+ // may be opened up in the future. |
|
| 39 |
+ |
|
| 40 |
+ // baseDelay is the amount of time to wait before retrying after the first |
|
| 41 |
+ // failure. |
|
| 42 |
+ baseDelay time.Duration |
|
| 43 |
+ |
|
| 44 |
+ // factor is applied to the backoff after each retry. |
|
| 45 |
+ factor float64 |
|
| 46 |
+ |
|
| 47 |
+ // jitter provides a range to randomize backoff delays. |
|
| 48 |
+ jitter float64 |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func setDefaults(bc *BackoffConfig) {
|
|
| 52 |
+ md := bc.MaxDelay |
|
| 53 |
+ *bc = DefaultBackoffConfig |
|
| 54 |
+ |
|
| 55 |
+ if md > 0 {
|
|
| 56 |
+ bc.MaxDelay = md |
|
| 57 |
+ } |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func (bc BackoffConfig) backoff(retries int) (t time.Duration) {
|
|
| 61 |
+ if retries == 0 {
|
|
| 62 |
+ return bc.baseDelay |
|
| 63 |
+ } |
|
| 64 |
+ backoff, max := float64(bc.baseDelay), float64(bc.MaxDelay) |
|
| 65 |
+ for backoff < max && retries > 0 {
|
|
| 66 |
+ backoff *= bc.factor |
|
| 67 |
+ retries-- |
|
| 68 |
+ } |
|
| 69 |
+ if backoff > max {
|
|
| 70 |
+ backoff = max |
|
| 71 |
+ } |
|
| 72 |
+ // Randomize backoff delays so that if a cluster of requests start at |
|
| 73 |
+ // the same time, they won't operate in lockstep. |
|
| 74 |
+ backoff *= 1 + bc.jitter*(rand.Float64()*2-1) |
|
| 75 |
+ if backoff < 0 {
|
|
| 76 |
+ return 0 |
|
| 77 |
+ } |
|
| 78 |
+ return time.Duration(backoff) |
|
| 79 |
+} |
| ... | ... |
@@ -34,13 +34,13 @@ |
| 34 | 34 |
package grpc |
| 35 | 35 |
|
| 36 | 36 |
import ( |
| 37 |
+ "bytes" |
|
| 37 | 38 |
"io" |
| 38 | 39 |
"time" |
| 39 | 40 |
|
| 40 | 41 |
"golang.org/x/net/context" |
| 41 | 42 |
"golang.org/x/net/trace" |
| 42 | 43 |
"google.golang.org/grpc/codes" |
| 43 |
- "google.golang.org/grpc/metadata" |
|
| 44 | 44 |
"google.golang.org/grpc/transport" |
| 45 | 45 |
) |
| 46 | 46 |
|
| ... | ... |
@@ -48,16 +48,16 @@ import ( |
| 48 | 48 |
// On error, it returns the error and indicates whether the call should be retried. |
| 49 | 49 |
// |
| 50 | 50 |
// TODO(zhaoq): Check whether the received message sequence is valid. |
| 51 |
-func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error {
|
|
| 51 |
+func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error {
|
|
| 52 | 52 |
// Try to acquire header metadata from the server if there is any. |
| 53 | 53 |
var err error |
| 54 | 54 |
c.headerMD, err = stream.Header() |
| 55 | 55 |
if err != nil {
|
| 56 | 56 |
return err |
| 57 | 57 |
} |
| 58 |
- p := &parser{s: stream}
|
|
| 58 |
+ p := &parser{r: stream}
|
|
| 59 | 59 |
for {
|
| 60 |
- if err = recv(p, codec, reply); err != nil {
|
|
| 60 |
+ if err = recv(p, dopts.codec, stream, dopts.dc, reply); err != nil {
|
|
| 61 | 61 |
if err == io.EOF {
|
| 62 | 62 |
break |
| 63 | 63 |
} |
| ... | ... |
@@ -69,7 +69,7 @@ func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream |
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 | 71 |
// sendRequest writes out various information of an RPC such as Context and Message. |
| 72 |
-func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
|
|
| 72 |
+func sendRequest(ctx context.Context, codec Codec, compressor Compressor, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
|
|
| 73 | 73 |
stream, err := t.NewStream(ctx, callHdr) |
| 74 | 74 |
if err != nil {
|
| 75 | 75 |
return nil, err |
| ... | ... |
@@ -81,8 +81,11 @@ func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t |
| 81 | 81 |
} |
| 82 | 82 |
} |
| 83 | 83 |
}() |
| 84 |
- // TODO(zhaoq): Support compression. |
|
| 85 |
- outBuf, err := encode(codec, args, compressionNone) |
|
| 84 |
+ var cbuf *bytes.Buffer |
|
| 85 |
+ if compressor != nil {
|
|
| 86 |
+ cbuf = new(bytes.Buffer) |
|
| 87 |
+ } |
|
| 88 |
+ outBuf, err := encode(codec, args, compressor, cbuf) |
|
| 86 | 89 |
if err != nil {
|
| 87 | 90 |
return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err) |
| 88 | 91 |
} |
| ... | ... |
@@ -94,16 +97,9 @@ func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t |
| 94 | 94 |
return stream, nil |
| 95 | 95 |
} |
| 96 | 96 |
|
| 97 |
-// callInfo contains all related configuration and information about an RPC. |
|
| 98 |
-type callInfo struct {
|
|
| 99 |
- failFast bool |
|
| 100 |
- headerMD metadata.MD |
|
| 101 |
- trailerMD metadata.MD |
|
| 102 |
- traceInfo traceInfo // in trace.go |
|
| 103 |
-} |
|
| 104 |
- |
|
| 105 |
-// Invoke is called by the generated code. It sends the RPC request on the |
|
| 106 |
-// wire and returns after response is received. |
|
| 97 |
+// Invoke sends the RPC request on the wire and returns after response is received. |
|
| 98 |
+// Invoke is called by generated code. Also users can call Invoke directly when it |
|
| 99 |
+// is really needed in their use cases. |
|
| 107 | 100 |
func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) {
|
| 108 | 101 |
var c callInfo |
| 109 | 102 |
for _, o := range opts {
|
| ... | ... |
@@ -153,6 +149,9 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
| 153 | 153 |
Host: cc.authority, |
| 154 | 154 |
Method: method, |
| 155 | 155 |
} |
| 156 |
+ if cc.dopts.cp != nil {
|
|
| 157 |
+ callHdr.SendCompress = cc.dopts.cp.Type() |
|
| 158 |
+ } |
|
| 156 | 159 |
t, err = cc.dopts.picker.Pick(ctx) |
| 157 | 160 |
if err != nil {
|
| 158 | 161 |
if lastErr != nil {
|
| ... | ... |
@@ -164,7 +163,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
| 164 | 164 |
if c.traceInfo.tr != nil {
|
| 165 | 165 |
c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
|
| 166 | 166 |
} |
| 167 |
- stream, err = sendRequest(ctx, cc.dopts.codec, callHdr, t, args, topts) |
|
| 167 |
+ stream, err = sendRequest(ctx, cc.dopts.codec, cc.dopts.cp, callHdr, t, args, topts) |
|
| 168 | 168 |
if err != nil {
|
| 169 | 169 |
if _, ok := err.(transport.ConnectionError); ok {
|
| 170 | 170 |
lastErr = err |
| ... | ... |
@@ -176,7 +175,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
| 176 | 176 |
return toRPCErr(err) |
| 177 | 177 |
} |
| 178 | 178 |
// Receive the response |
| 179 |
- lastErr = recvResponse(cc.dopts.codec, t, &c, stream, reply) |
|
| 179 |
+ lastErr = recvResponse(cc.dopts, t, &c, stream, reply) |
|
| 180 | 180 |
if _, ok := lastErr.(transport.ConnectionError); ok {
|
| 181 | 181 |
continue |
| 182 | 182 |
} |
| ... | ... |
@@ -187,6 +186,6 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
| 187 | 187 |
if lastErr != nil {
|
| 188 | 188 |
return toRPCErr(lastErr) |
| 189 | 189 |
} |
| 190 |
- return Errorf(stream.StatusCode(), stream.StatusDesc()) |
|
| 190 |
+ return Errorf(stream.StatusCode(), "%s", stream.StatusDesc()) |
|
| 191 | 191 |
} |
| 192 | 192 |
} |
| ... | ... |
@@ -52,10 +52,10 @@ var ( |
| 52 | 52 |
// ErrUnspecTarget indicates that the target address is unspecified. |
| 53 | 53 |
ErrUnspecTarget = errors.New("grpc: target is unspecified")
|
| 54 | 54 |
// ErrNoTransportSecurity indicates that there is no transport security |
| 55 |
- // being set for ClientConn. Users should either set one or explicityly |
|
| 55 |
+ // being set for ClientConn. Users should either set one or explicitly |
|
| 56 | 56 |
// call WithInsecure DialOption to disable security. |
| 57 | 57 |
ErrNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)")
|
| 58 |
- // ErrCredentialsMisuse indicates that users want to transmit security infomation |
|
| 58 |
+ // ErrCredentialsMisuse indicates that users want to transmit security information |
|
| 59 | 59 |
// (e.g., oauth2 token) which requires secure connection on an insecure |
| 60 | 60 |
// connection. |
| 61 | 61 |
ErrCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)")
|
| ... | ... |
@@ -73,6 +73,9 @@ var ( |
| 73 | 73 |
// values passed to Dial. |
| 74 | 74 |
type dialOptions struct {
|
| 75 | 75 |
codec Codec |
| 76 |
+ cp Compressor |
|
| 77 |
+ dc Decompressor |
|
| 78 |
+ bs backoffStrategy |
|
| 76 | 79 |
picker Picker |
| 77 | 80 |
block bool |
| 78 | 81 |
insecure bool |
| ... | ... |
@@ -89,12 +92,57 @@ func WithCodec(c Codec) DialOption {
|
| 89 | 89 |
} |
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 |
+// WithCompressor returns a DialOption which sets a CompressorGenerator for generating message |
|
| 93 |
+// compressor. |
|
| 94 |
+func WithCompressor(cp Compressor) DialOption {
|
|
| 95 |
+ return func(o *dialOptions) {
|
|
| 96 |
+ o.cp = cp |
|
| 97 |
+ } |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+// WithDecompressor returns a DialOption which sets a DecompressorGenerator for generating |
|
| 101 |
+// message decompressor. |
|
| 102 |
+func WithDecompressor(dc Decompressor) DialOption {
|
|
| 103 |
+ return func(o *dialOptions) {
|
|
| 104 |
+ o.dc = dc |
|
| 105 |
+ } |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+// WithPicker returns a DialOption which sets a picker for connection selection. |
|
| 92 | 109 |
func WithPicker(p Picker) DialOption {
|
| 93 | 110 |
return func(o *dialOptions) {
|
| 94 | 111 |
o.picker = p |
| 95 | 112 |
} |
| 96 | 113 |
} |
| 97 | 114 |
|
| 115 |
+// WithBackoffMaxDelay configures the dialer to use the provided maximum delay |
|
| 116 |
+// when backing off after failed connection attempts. |
|
| 117 |
+func WithBackoffMaxDelay(md time.Duration) DialOption {
|
|
| 118 |
+ return WithBackoffConfig(BackoffConfig{MaxDelay: md})
|
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+// WithBackoffConfig configures the dialer to use the provided backoff |
|
| 122 |
+// parameters after connection failures. |
|
| 123 |
+// |
|
| 124 |
+// Use WithBackoffMaxDelay until more parameters on BackoffConfig are opened up |
|
| 125 |
+// for use. |
|
| 126 |
+func WithBackoffConfig(b BackoffConfig) DialOption {
|
|
| 127 |
+ // Set defaults to ensure that provided BackoffConfig is valid and |
|
| 128 |
+ // unexported fields get default values. |
|
| 129 |
+ setDefaults(&b) |
|
| 130 |
+ return withBackoff(b) |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+// withBackoff sets the backoff strategy used for retries after a |
|
| 134 |
+// failed connection attempt. |
|
| 135 |
+// |
|
| 136 |
+// This can be exported if arbitrary backoff strategies are allowed by GRPC. |
|
| 137 |
+func withBackoff(bs backoffStrategy) DialOption {
|
|
| 138 |
+ return func(o *dialOptions) {
|
|
| 139 |
+ o.bs = bs |
|
| 140 |
+ } |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 98 | 143 |
// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying |
| 99 | 144 |
// connection is up. Without this, Dial returns immediately and connecting the server |
| 100 | 145 |
// happens in background. |
| ... | ... |
@@ -104,6 +152,8 @@ func WithBlock() DialOption {
|
| 104 | 104 |
} |
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 |
+// WithInsecure returns a DialOption which disables transport security for this ClientConn. |
|
| 108 |
+// Note that transport security is required unless WithInsecure is set. |
|
| 107 | 109 |
func WithInsecure() DialOption {
|
| 108 | 110 |
return func(o *dialOptions) {
|
| 109 | 111 |
o.insecure = true |
| ... | ... |
@@ -159,6 +209,11 @@ func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
| 159 | 159 |
// Set the default codec. |
| 160 | 160 |
cc.dopts.codec = protoCodec{}
|
| 161 | 161 |
} |
| 162 |
+ |
|
| 163 |
+ if cc.dopts.bs == nil {
|
|
| 164 |
+ cc.dopts.bs = DefaultBackoffConfig |
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 162 | 167 |
if cc.dopts.picker == nil {
|
| 163 | 168 |
cc.dopts.picker = &unicastPicker{
|
| 164 | 169 |
target: target, |
| ... | ... |
@@ -267,10 +322,9 @@ func NewConn(cc *ClientConn) (*Conn, error) {
|
| 267 | 267 |
if !c.dopts.insecure {
|
| 268 | 268 |
var ok bool |
| 269 | 269 |
for _, cd := range c.dopts.copts.AuthOptions {
|
| 270 |
- if _, ok := cd.(credentials.TransportAuthenticator); !ok {
|
|
| 271 |
- continue |
|
| 270 |
+ if _, ok = cd.(credentials.TransportAuthenticator); ok {
|
|
| 271 |
+ break |
|
| 272 | 272 |
} |
| 273 |
- ok = true |
|
| 274 | 273 |
} |
| 275 | 274 |
if !ok {
|
| 276 | 275 |
return nil, ErrNoTransportSecurity |
| ... | ... |
@@ -395,7 +449,7 @@ func (cc *Conn) resetTransport(closeTransport bool) error {
|
| 395 | 395 |
return ErrClientConnTimeout |
| 396 | 396 |
} |
| 397 | 397 |
} |
| 398 |
- sleepTime := backoff(retries) |
|
| 398 |
+ sleepTime := cc.dopts.bs.backoff(retries) |
|
| 399 | 399 |
timeout := sleepTime |
| 400 | 400 |
if timeout < minConnectTimeout {
|
| 401 | 401 |
timeout = minConnectTimeout |
| ... | ... |
@@ -518,8 +572,9 @@ func (cc *Conn) Wait(ctx context.Context) (transport.ClientTransport, error) {
|
| 518 | 518 |
cc.mu.Unlock() |
| 519 | 519 |
return nil, ErrClientConnClosing |
| 520 | 520 |
case cc.state == Ready: |
| 521 |
+ ct := cc.transport |
|
| 521 | 522 |
cc.mu.Unlock() |
| 522 |
- return cc.transport, nil |
|
| 523 |
+ return ct, nil |
|
| 523 | 524 |
default: |
| 524 | 525 |
ready := cc.ready |
| 525 | 526 |
if ready == nil {
|
| ... | ... |
@@ -4,15 +4,20 @@ set -e |
| 4 | 4 |
|
| 5 | 5 |
workdir=.cover |
| 6 | 6 |
profile="$workdir/cover.out" |
| 7 |
-mode=count |
|
| 7 |
+mode=set |
|
| 8 |
+end2endtest="google.golang.org/grpc/test" |
|
| 8 | 9 |
|
| 9 | 10 |
generate_cover_data() {
|
| 10 | 11 |
rm -rf "$workdir" |
| 11 | 12 |
mkdir "$workdir" |
| 12 | 13 |
|
| 13 | 14 |
for pkg in "$@"; do |
| 14 |
- f="$workdir/$(echo $pkg | tr / -).cover" |
|
| 15 |
- go test -covermode="$mode" -coverprofile="$f" "$pkg" |
|
| 15 |
+ if [ $pkg == "google.golang.org/grpc" -o $pkg == "google.golang.org/grpc/transport" -o $pkg == "google.golang.org/grpc/metadata" -o $pkg == "google.golang.org/grpc/credentials" ] |
|
| 16 |
+ then |
|
| 17 |
+ f="$workdir/$(echo $pkg | tr / -)" |
|
| 18 |
+ go test -covermode="$mode" -coverprofile="$f.cover" "$pkg" |
|
| 19 |
+ go test -covermode="$mode" -coverpkg "$pkg" -coverprofile="$f.e2e.cover" "$end2endtest" |
|
| 20 |
+ fi |
|
| 16 | 21 |
done |
| 17 | 22 |
|
| 18 | 23 |
echo "mode: $mode" >"$profile" |
| ... | ... |
@@ -32,6 +37,8 @@ show_cover_report func |
| 32 | 32 |
case "$1" in |
| 33 | 33 |
"") |
| 34 | 34 |
;; |
| 35 |
+--html) |
|
| 36 |
+ show_cover_report html ;; |
|
| 35 | 37 |
--coveralls) |
| 36 | 38 |
push_to_coveralls ;; |
| 37 | 39 |
*) |
| ... | ... |
@@ -87,19 +87,6 @@ type AuthInfo interface {
|
| 87 | 87 |
AuthType() string |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
-type authInfoKey struct{}
|
|
| 91 |
- |
|
| 92 |
-// NewContext creates a new context with authInfo attached. |
|
| 93 |
-func NewContext(ctx context.Context, authInfo AuthInfo) context.Context {
|
|
| 94 |
- return context.WithValue(ctx, authInfoKey{}, authInfo)
|
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-// FromContext returns the authInfo in ctx if it exists. |
|
| 98 |
-func FromContext(ctx context.Context) (authInfo AuthInfo, ok bool) {
|
|
| 99 |
- authInfo, ok = ctx.Value(authInfoKey{}).(AuthInfo)
|
|
| 100 |
- return |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 | 90 |
// TransportAuthenticator defines the common interface for all the live gRPC wire |
| 104 | 91 |
// protocols and supported transport security protocols (e.g., TLS, SSL). |
| 105 | 92 |
type TransportAuthenticator interface {
|
| ... | ... |
@@ -42,6 +42,8 @@ import ( |
| 42 | 42 |
) |
| 43 | 43 |
|
| 44 | 44 |
// Use golang's standard logger by default. |
| 45 |
+// Access is not mutex-protected: do not modify except in init() |
|
| 46 |
+// functions. |
|
| 45 | 47 |
var logger Logger = log.New(os.Stderr, "", log.LstdFlags) |
| 46 | 48 |
|
| 47 | 49 |
// Logger mimics golang's standard Logger as an interface. |
| ... | ... |
@@ -54,7 +56,8 @@ type Logger interface {
|
| 54 | 54 |
Println(args ...interface{})
|
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 |
-// SetLogger sets the logger that is used in grpc. |
|
| 57 |
+// SetLogger sets the logger that is used in grpc. Call only from |
|
| 58 |
+// init() functions. |
|
| 58 | 59 |
func SetLogger(l Logger) {
|
| 59 | 60 |
logger = l |
| 60 | 61 |
} |
| 61 | 62 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,74 @@ |
| 0 |
+/* |
|
| 1 |
+ * |
|
| 2 |
+ * Copyright 2016, Google Inc. |
|
| 3 |
+ * All rights reserved. |
|
| 4 |
+ * |
|
| 5 |
+ * Redistribution and use in source and binary forms, with or without |
|
| 6 |
+ * modification, are permitted provided that the following conditions are |
|
| 7 |
+ * met: |
|
| 8 |
+ * |
|
| 9 |
+ * * Redistributions of source code must retain the above copyright |
|
| 10 |
+ * notice, this list of conditions and the following disclaimer. |
|
| 11 |
+ * * Redistributions in binary form must reproduce the above |
|
| 12 |
+ * copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+ * in the documentation and/or other materials provided with the |
|
| 14 |
+ * distribution. |
|
| 15 |
+ * * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+ * contributors may be used to endorse or promote products derived from |
|
| 17 |
+ * this software without specific prior written permission. |
|
| 18 |
+ * |
|
| 19 |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ * |
|
| 31 |
+ */ |
|
| 32 |
+ |
|
| 33 |
+package grpc |
|
| 34 |
+ |
|
| 35 |
+import ( |
|
| 36 |
+ "golang.org/x/net/context" |
|
| 37 |
+) |
|
| 38 |
+ |
|
| 39 |
+// UnaryServerInfo consists of various information about a unary RPC on |
|
| 40 |
+// server side. All per-rpc information may be mutated by the interceptor. |
|
| 41 |
+type UnaryServerInfo struct {
|
|
| 42 |
+ // Server is the service implementation the user provides. This is read-only. |
|
| 43 |
+ Server interface{}
|
|
| 44 |
+ // FullMethod is the full RPC method string, i.e., /package.service/method. |
|
| 45 |
+ FullMethod string |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal |
|
| 49 |
+// execution of a unary RPC. |
|
| 50 |
+type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)
|
|
| 51 |
+ |
|
| 52 |
+// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info |
|
| 53 |
+// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper |
|
| 54 |
+// of the service method implementation. It is the responsibility of the interceptor to invoke handler |
|
| 55 |
+// to complete the RPC. |
|
| 56 |
+type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
|
|
| 57 |
+ |
|
| 58 |
+// StreamServerInfo consists of various information about a streaming RPC on |
|
| 59 |
+// server side. All per-rpc information may be mutated by the interceptor. |
|
| 60 |
+type StreamServerInfo struct {
|
|
| 61 |
+ // FullMethod is the full RPC method string, i.e., /package.service/method. |
|
| 62 |
+ FullMethod string |
|
| 63 |
+ // IsClientStream indicates whether the RPC is a client streaming RPC. |
|
| 64 |
+ IsClientStream bool |
|
| 65 |
+ // IsServerStream indicates whether the RPC is a server streaming RPC. |
|
| 66 |
+ IsServerStream bool |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server. |
|
| 70 |
+// info contains all the information of this RPC the interceptor can operate on. And handler is the |
|
| 71 |
+// service method implementation. It is the responsibility of the interceptor to invoke handler to |
|
| 72 |
+// complete the RPC. |
|
| 73 |
+type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
|
| 0 | 74 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,49 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright 2016, Google Inc. |
|
| 2 |
+ * All rights reserved. |
|
| 3 |
+ * |
|
| 4 |
+ * Redistribution and use in source and binary forms, with or without |
|
| 5 |
+ * modification, are permitted provided that the following conditions are |
|
| 6 |
+ * met: |
|
| 7 |
+ * |
|
| 8 |
+ * * Redistributions of source code must retain the above copyright |
|
| 9 |
+ * notice, this list of conditions and the following disclaimer. |
|
| 10 |
+ * * Redistributions in binary form must reproduce the above |
|
| 11 |
+ * copyright notice, this list of conditions and the following disclaimer |
|
| 12 |
+ * in the documentation and/or other materials provided with the |
|
| 13 |
+ * distribution. |
|
| 14 |
+ * * Neither the name of Google Inc. nor the names of its |
|
| 15 |
+ * contributors may be used to endorse or promote products derived from |
|
| 16 |
+ * this software without specific prior written permission. |
|
| 17 |
+ * |
|
| 18 |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 19 |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 20 |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 21 |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 22 |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 23 |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 24 |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 25 |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 26 |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 27 |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 28 |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 29 |
+ * |
|
| 30 |
+ */ |
|
| 31 |
+ |
|
| 32 |
+// Package internal contains gRPC-internal code for testing, to avoid polluting |
|
| 33 |
+// the godoc of the top-level grpc package. |
|
| 34 |
+package internal |
|
| 35 |
+ |
|
| 36 |
+// TestingCloseConns closes all existing transports but keeps |
|
| 37 |
+// grpcServer.lis accepting new connections. |
|
| 38 |
+// |
|
| 39 |
+// The provided grpcServer must be of type *grpc.Server. It is untyped |
|
| 40 |
+// for circular dependency reasons. |
|
| 41 |
+var TestingCloseConns func(grpcServer interface{})
|
|
| 42 |
+ |
|
| 43 |
+// TestingUseHandlerImpl enables the http.Handler-based server implementation. |
|
| 44 |
+// It must be called before Serve and requires TLS credentials. |
|
| 45 |
+// |
|
| 46 |
+// The provided grpcServer must be of type *grpc.Server. It is untyped |
|
| 47 |
+// for circular dependency reasons. |
|
| 48 |
+var TestingUseHandlerImpl func(grpcServer interface{})
|
| 0 | 49 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 0 |
+/* |
|
| 1 |
+ * |
|
| 2 |
+ * Copyright 2014, Google Inc. |
|
| 3 |
+ * All rights reserved. |
|
| 4 |
+ * |
|
| 5 |
+ * Redistribution and use in source and binary forms, with or without |
|
| 6 |
+ * modification, are permitted provided that the following conditions are |
|
| 7 |
+ * met: |
|
| 8 |
+ * |
|
| 9 |
+ * * Redistributions of source code must retain the above copyright |
|
| 10 |
+ * notice, this list of conditions and the following disclaimer. |
|
| 11 |
+ * * Redistributions in binary form must reproduce the above |
|
| 12 |
+ * copyright notice, this list of conditions and the following disclaimer |
|
| 13 |
+ * in the documentation and/or other materials provided with the |
|
| 14 |
+ * distribution. |
|
| 15 |
+ * * Neither the name of Google Inc. nor the names of its |
|
| 16 |
+ * contributors may be used to endorse or promote products derived from |
|
| 17 |
+ * this software without specific prior written permission. |
|
| 18 |
+ * |
|
| 19 |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 20 |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 21 |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 22 |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 23 |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 24 |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 25 |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 26 |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 27 |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 28 |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 29 |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 30 |
+ * |
|
| 31 |
+ */ |
|
| 32 |
+ |
|
| 33 |
+// Package peer defines various peer information associated with RPCs and |
|
| 34 |
+// corresponding utils. |
|
| 35 |
+package peer |
|
| 36 |
+ |
|
| 37 |
+import ( |
|
| 38 |
+ "net" |
|
| 39 |
+ |
|
| 40 |
+ "golang.org/x/net/context" |
|
| 41 |
+ "google.golang.org/grpc/credentials" |
|
| 42 |
+) |
|
| 43 |
+ |
|
| 44 |
+// Peer contains the information of the peer for an RPC. |
|
| 45 |
+type Peer struct {
|
|
| 46 |
+ // Addr is the peer address. |
|
| 47 |
+ Addr net.Addr |
|
| 48 |
+ // AuthInfo is the authentication information of the transport. |
|
| 49 |
+ // It is nil if there is no transport security being used. |
|
| 50 |
+ AuthInfo credentials.AuthInfo |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+type peerKey struct{}
|
|
| 54 |
+ |
|
| 55 |
+// NewContext creates a new context with peer information attached. |
|
| 56 |
+func NewContext(ctx context.Context, p *Peer) context.Context {
|
|
| 57 |
+ return context.WithValue(ctx, peerKey{}, p)
|
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// FromContext returns the peer information in ctx if it exists. |
|
| 61 |
+func FromContext(ctx context.Context) (p *Peer, ok bool) {
|
|
| 62 |
+ p, ok = ctx.Value(peerKey{}).(*Peer)
|
|
| 63 |
+ return |
|
| 64 |
+} |
| ... | ... |
@@ -34,13 +34,14 @@ |
| 34 | 34 |
package grpc |
| 35 | 35 |
|
| 36 | 36 |
import ( |
| 37 |
+ "bytes" |
|
| 38 |
+ "compress/gzip" |
|
| 37 | 39 |
"encoding/binary" |
| 38 | 40 |
"fmt" |
| 39 | 41 |
"io" |
| 42 |
+ "io/ioutil" |
|
| 40 | 43 |
"math" |
| 41 |
- "math/rand" |
|
| 42 | 44 |
"os" |
| 43 |
- "time" |
|
| 44 | 45 |
|
| 45 | 46 |
"github.com/golang/protobuf/proto" |
| 46 | 47 |
"golang.org/x/net/context" |
| ... | ... |
@@ -75,6 +76,71 @@ func (protoCodec) String() string {
|
| 75 | 75 |
return "proto" |
| 76 | 76 |
} |
| 77 | 77 |
|
| 78 |
+// Compressor defines the interface gRPC uses to compress a message. |
|
| 79 |
+type Compressor interface {
|
|
| 80 |
+ // Do compresses p into w. |
|
| 81 |
+ Do(w io.Writer, p []byte) error |
|
| 82 |
+ // Type returns the compression algorithm the Compressor uses. |
|
| 83 |
+ Type() string |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+// NewGZIPCompressor creates a Compressor based on GZIP. |
|
| 87 |
+func NewGZIPCompressor() Compressor {
|
|
| 88 |
+ return &gzipCompressor{}
|
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+type gzipCompressor struct {
|
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+func (c *gzipCompressor) Do(w io.Writer, p []byte) error {
|
|
| 95 |
+ z := gzip.NewWriter(w) |
|
| 96 |
+ if _, err := z.Write(p); err != nil {
|
|
| 97 |
+ return err |
|
| 98 |
+ } |
|
| 99 |
+ return z.Close() |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+func (c *gzipCompressor) Type() string {
|
|
| 103 |
+ return "gzip" |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// Decompressor defines the interface gRPC uses to decompress a message. |
|
| 107 |
+type Decompressor interface {
|
|
| 108 |
+ // Do reads the data from r and uncompress them. |
|
| 109 |
+ Do(r io.Reader) ([]byte, error) |
|
| 110 |
+ // Type returns the compression algorithm the Decompressor uses. |
|
| 111 |
+ Type() string |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+type gzipDecompressor struct {
|
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// NewGZIPDecompressor creates a Decompressor based on GZIP. |
|
| 118 |
+func NewGZIPDecompressor() Decompressor {
|
|
| 119 |
+ return &gzipDecompressor{}
|
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
|
|
| 123 |
+ z, err := gzip.NewReader(r) |
|
| 124 |
+ if err != nil {
|
|
| 125 |
+ return nil, err |
|
| 126 |
+ } |
|
| 127 |
+ defer z.Close() |
|
| 128 |
+ return ioutil.ReadAll(z) |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (d *gzipDecompressor) Type() string {
|
|
| 132 |
+ return "gzip" |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// callInfo contains all related configuration and information about an RPC. |
|
| 136 |
+type callInfo struct {
|
|
| 137 |
+ failFast bool |
|
| 138 |
+ headerMD metadata.MD |
|
| 139 |
+ trailerMD metadata.MD |
|
| 140 |
+ traceInfo traceInfo // in trace.go |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 78 | 143 |
// CallOption configures a Call before it starts or extracts information from |
| 79 | 144 |
// a Call after it completes. |
| 80 | 145 |
type CallOption interface {
|
| ... | ... |
@@ -118,36 +184,49 @@ type payloadFormat uint8 |
| 118 | 118 |
|
| 119 | 119 |
const ( |
| 120 | 120 |
compressionNone payloadFormat = iota // no compression |
| 121 |
- compressionFlate |
|
| 122 |
- // More formats |
|
| 121 |
+ compressionMade |
|
| 123 | 122 |
) |
| 124 | 123 |
|
| 125 | 124 |
// parser reads complelete gRPC messages from the underlying reader. |
| 126 | 125 |
type parser struct {
|
| 127 |
- s io.Reader |
|
| 128 |
-} |
|
| 126 |
+ // r is the underlying reader. |
|
| 127 |
+ // See the comment on recvMsg for the permissible |
|
| 128 |
+ // error types. |
|
| 129 |
+ r io.Reader |
|
| 129 | 130 |
|
| 130 |
-// recvMsg is to read a complete gRPC message from the stream. It is blocking if |
|
| 131 |
-// the message has not been complete yet. It returns the message and its type, |
|
| 132 |
-// EOF is returned with nil msg and 0 pf if the entire stream is done. Other |
|
| 133 |
-// non-nil error is returned if something is wrong on reading. |
|
| 134 |
-func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
|
|
| 135 | 131 |
// The header of a gRPC message. Find more detail |
| 136 | 132 |
// at http://www.grpc.io/docs/guides/wire.html. |
| 137 |
- var buf [5]byte |
|
| 133 |
+ header [5]byte |
|
| 134 |
+} |
|
| 138 | 135 |
|
| 139 |
- if _, err := io.ReadFull(p.s, buf[:]); err != nil {
|
|
| 136 |
+// recvMsg reads a complete gRPC message from the stream. |
|
| 137 |
+// |
|
| 138 |
+// It returns the message and its payload (compression/encoding) |
|
| 139 |
+// format. The caller owns the returned msg memory. |
|
| 140 |
+// |
|
| 141 |
+// If there is an error, possible values are: |
|
| 142 |
+// * io.EOF, when no messages remain |
|
| 143 |
+// * io.ErrUnexpectedEOF |
|
| 144 |
+// * of type transport.ConnectionError |
|
| 145 |
+// * of type transport.StreamError |
|
| 146 |
+// No other error values or types must be returned, which also means |
|
| 147 |
+// that the underlying io.Reader must not return an incompatible |
|
| 148 |
+// error. |
|
| 149 |
+func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
|
|
| 150 |
+ if _, err := io.ReadFull(p.r, p.header[:]); err != nil {
|
|
| 140 | 151 |
return 0, nil, err |
| 141 | 152 |
} |
| 142 | 153 |
|
| 143 |
- pf = payloadFormat(buf[0]) |
|
| 144 |
- length := binary.BigEndian.Uint32(buf[1:]) |
|
| 154 |
+ pf = payloadFormat(p.header[0]) |
|
| 155 |
+ length := binary.BigEndian.Uint32(p.header[1:]) |
|
| 145 | 156 |
|
| 146 | 157 |
if length == 0 {
|
| 147 | 158 |
return pf, nil, nil |
| 148 | 159 |
} |
| 160 |
+ // TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead |
|
| 161 |
+ // of making it for each message: |
|
| 149 | 162 |
msg = make([]byte, int(length)) |
| 150 |
- if _, err := io.ReadFull(p.s, msg); err != nil {
|
|
| 163 |
+ if _, err := io.ReadFull(p.r, msg); err != nil {
|
|
| 151 | 164 |
if err == io.EOF {
|
| 152 | 165 |
err = io.ErrUnexpectedEOF |
| 153 | 166 |
} |
| ... | ... |
@@ -158,7 +237,7 @@ func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
|
| 158 | 158 |
|
| 159 | 159 |
// encode serializes msg and prepends the message header. If msg is nil, it |
| 160 | 160 |
// generates the message header of 0 message length. |
| 161 |
-func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
|
|
| 161 |
+func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte, error) {
|
|
| 162 | 162 |
var b []byte |
| 163 | 163 |
var length uint |
| 164 | 164 |
if msg != nil {
|
| ... | ... |
@@ -168,6 +247,12 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
|
| 168 | 168 |
if err != nil {
|
| 169 | 169 |
return nil, err |
| 170 | 170 |
} |
| 171 |
+ if cp != nil {
|
|
| 172 |
+ if err := cp.Do(cbuf, b); err != nil {
|
|
| 173 |
+ return nil, err |
|
| 174 |
+ } |
|
| 175 |
+ b = cbuf.Bytes() |
|
| 176 |
+ } |
|
| 171 | 177 |
length = uint(len(b)) |
| 172 | 178 |
} |
| 173 | 179 |
if length > math.MaxUint32 {
|
| ... | ... |
@@ -182,7 +267,11 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
|
| 182 | 182 |
var buf = make([]byte, payloadLen+sizeLen+len(b)) |
| 183 | 183 |
|
| 184 | 184 |
// Write payload format |
| 185 |
- buf[0] = byte(pf) |
|
| 185 |
+ if cp == nil {
|
|
| 186 |
+ buf[0] = byte(compressionNone) |
|
| 187 |
+ } else {
|
|
| 188 |
+ buf[0] = byte(compressionMade) |
|
| 189 |
+ } |
|
| 186 | 190 |
// Write length of b into buf |
| 187 | 191 |
binary.BigEndian.PutUint32(buf[1:], uint32(length)) |
| 188 | 192 |
// Copy encoded msg to buf |
| ... | ... |
@@ -191,22 +280,38 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
|
| 191 | 191 |
return buf, nil |
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 |
-func recv(p *parser, c Codec, m interface{}) error {
|
|
| 194 |
+func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) error {
|
|
| 195 |
+ switch pf {
|
|
| 196 |
+ case compressionNone: |
|
| 197 |
+ case compressionMade: |
|
| 198 |
+ if recvCompress == "" {
|
|
| 199 |
+ return transport.StreamErrorf(codes.InvalidArgument, "grpc: invalid grpc-encoding %q with compression enabled", recvCompress) |
|
| 200 |
+ } |
|
| 201 |
+ if dc == nil || recvCompress != dc.Type() {
|
|
| 202 |
+ return transport.StreamErrorf(codes.InvalidArgument, "grpc: Decompressor is not installed for grpc-encoding %q", recvCompress) |
|
| 203 |
+ } |
|
| 204 |
+ default: |
|
| 205 |
+ return transport.StreamErrorf(codes.InvalidArgument, "grpc: received unexpected payload format %d", pf) |
|
| 206 |
+ } |
|
| 207 |
+ return nil |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}) error {
|
|
| 195 | 211 |
pf, d, err := p.recvMsg() |
| 196 | 212 |
if err != nil {
|
| 197 | 213 |
return err |
| 198 | 214 |
} |
| 199 |
- switch pf {
|
|
| 200 |
- case compressionNone: |
|
| 201 |
- if err := c.Unmarshal(d, m); err != nil {
|
|
| 202 |
- if rErr, ok := err.(rpcError); ok {
|
|
| 203 |
- return rErr |
|
| 204 |
- } else {
|
|
| 205 |
- return Errorf(codes.Internal, "grpc: %v", err) |
|
| 206 |
- } |
|
| 215 |
+ if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
|
|
| 216 |
+ return err |
|
| 217 |
+ } |
|
| 218 |
+ if pf == compressionMade {
|
|
| 219 |
+ d, err = dc.Do(bytes.NewReader(d)) |
|
| 220 |
+ if err != nil {
|
|
| 221 |
+ return transport.StreamErrorf(codes.Internal, "grpc: failed to decompress the received message %v", err) |
|
| 207 | 222 |
} |
| 208 |
- default: |
|
| 209 |
- return Errorf(codes.Internal, "gprc: compression is not supported yet.") |
|
| 223 |
+ } |
|
| 224 |
+ if err := c.Unmarshal(d, m); err != nil {
|
|
| 225 |
+ return transport.StreamErrorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err) |
|
| 210 | 226 |
} |
| 211 | 227 |
return nil |
| 212 | 228 |
} |
| ... | ... |
@@ -218,7 +323,7 @@ type rpcError struct {
|
| 218 | 218 |
} |
| 219 | 219 |
|
| 220 | 220 |
func (e rpcError) Error() string {
|
| 221 |
- return fmt.Sprintf("rpc error: code = %d desc = %q", e.code, e.desc)
|
|
| 221 |
+ return fmt.Sprintf("rpc error: code = %d desc = %s", e.code, e.desc)
|
|
| 222 | 222 |
} |
| 223 | 223 |
|
| 224 | 224 |
// Code returns the error code for err if it was produced by the rpc system. |
| ... | ... |
@@ -304,34 +409,10 @@ func convertCode(err error) codes.Code {
|
| 304 | 304 |
return codes.Unknown |
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 |
-const ( |
|
| 308 |
- // how long to wait after the first failure before retrying |
|
| 309 |
- baseDelay = 1.0 * time.Second |
|
| 310 |
- // upper bound of backoff delay |
|
| 311 |
- maxDelay = 120 * time.Second |
|
| 312 |
- // backoff increases by this factor on each retry |
|
| 313 |
- backoffFactor = 1.6 |
|
| 314 |
- // backoff is randomized downwards by this factor |
|
| 315 |
- backoffJitter = 0.2 |
|
| 316 |
-) |
|
| 317 |
- |
|
| 318 |
-func backoff(retries int) (t time.Duration) {
|
|
| 319 |
- if retries == 0 {
|
|
| 320 |
- return baseDelay |
|
| 321 |
- } |
|
| 322 |
- backoff, max := float64(baseDelay), float64(maxDelay) |
|
| 323 |
- for backoff < max && retries > 0 {
|
|
| 324 |
- backoff *= backoffFactor |
|
| 325 |
- retries-- |
|
| 326 |
- } |
|
| 327 |
- if backoff > max {
|
|
| 328 |
- backoff = max |
|
| 329 |
- } |
|
| 330 |
- // Randomize backoff delays so that if a cluster of requests start at |
|
| 331 |
- // the same time, they won't operate in lockstep. |
|
| 332 |
- backoff *= 1 + backoffJitter*(rand.Float64()*2-1) |
|
| 333 |
- if backoff < 0 {
|
|
| 334 |
- return 0 |
|
| 335 |
- } |
|
| 336 |
- return time.Duration(backoff) |
|
| 337 |
-} |
|
| 307 |
+// SupportPackageIsVersion2 is referenced from generated protocol buffer files |
|
| 308 |
+// to assert that that code is compatible with this version of the grpc package. |
|
| 309 |
+// |
|
| 310 |
+// This constant may be renamed in the future if a change in the generated code |
|
| 311 |
+// requires a synchronised update of grpc-go and protoc-gen-go. This constant |
|
| 312 |
+// should not be referenced from any other code. |
|
| 313 |
+const SupportPackageIsVersion2 = true |
| ... | ... |
@@ -34,10 +34,12 @@ |
| 34 | 34 |
package grpc |
| 35 | 35 |
|
| 36 | 36 |
import ( |
| 37 |
+ "bytes" |
|
| 37 | 38 |
"errors" |
| 38 | 39 |
"fmt" |
| 39 | 40 |
"io" |
| 40 | 41 |
"net" |
| 42 |
+ "net/http" |
|
| 41 | 43 |
"reflect" |
| 42 | 44 |
"runtime" |
| 43 | 45 |
"strings" |
| ... | ... |
@@ -45,15 +47,17 @@ import ( |
| 45 | 45 |
"time" |
| 46 | 46 |
|
| 47 | 47 |
"golang.org/x/net/context" |
| 48 |
+ "golang.org/x/net/http2" |
|
| 48 | 49 |
"golang.org/x/net/trace" |
| 49 | 50 |
"google.golang.org/grpc/codes" |
| 50 | 51 |
"google.golang.org/grpc/credentials" |
| 51 | 52 |
"google.golang.org/grpc/grpclog" |
| 53 |
+ "google.golang.org/grpc/internal" |
|
| 52 | 54 |
"google.golang.org/grpc/metadata" |
| 53 | 55 |
"google.golang.org/grpc/transport" |
| 54 | 56 |
) |
| 55 | 57 |
|
| 56 |
-type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error)
|
|
| 58 |
+type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
|
|
| 57 | 59 |
|
| 58 | 60 |
// MethodDesc represents an RPC service's method specification. |
| 59 | 61 |
type MethodDesc struct {
|
| ... | ... |
@@ -81,10 +85,11 @@ type service struct {
|
| 81 | 81 |
|
| 82 | 82 |
// Server is a gRPC server to serve RPC requests. |
| 83 | 83 |
type Server struct {
|
| 84 |
- opts options |
|
| 85 |
- mu sync.Mutex |
|
| 84 |
+ opts options |
|
| 85 |
+ |
|
| 86 |
+ mu sync.Mutex // guards following |
|
| 86 | 87 |
lis map[net.Listener]bool |
| 87 |
- conns map[transport.ServerTransport]bool |
|
| 88 |
+ conns map[io.Closer]bool |
|
| 88 | 89 |
m map[string]*service // service name -> service info |
| 89 | 90 |
events trace.EventLog |
| 90 | 91 |
} |
| ... | ... |
@@ -92,7 +97,12 @@ type Server struct {
|
| 92 | 92 |
type options struct {
|
| 93 | 93 |
creds credentials.Credentials |
| 94 | 94 |
codec Codec |
| 95 |
+ cp Compressor |
|
| 96 |
+ dc Decompressor |
|
| 97 |
+ unaryInt UnaryServerInterceptor |
|
| 98 |
+ streamInt StreamServerInterceptor |
|
| 95 | 99 |
maxConcurrentStreams uint32 |
| 100 |
+ useHandlerImpl bool // use http.Handler-based server |
|
| 96 | 101 |
} |
| 97 | 102 |
|
| 98 | 103 |
// A ServerOption sets options. |
| ... | ... |
@@ -105,6 +115,18 @@ func CustomCodec(codec Codec) ServerOption {
|
| 105 | 105 |
} |
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 |
+func RPCCompressor(cp Compressor) ServerOption {
|
|
| 109 |
+ return func(o *options) {
|
|
| 110 |
+ o.cp = cp |
|
| 111 |
+ } |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func RPCDecompressor(dc Decompressor) ServerOption {
|
|
| 115 |
+ return func(o *options) {
|
|
| 116 |
+ o.dc = dc |
|
| 117 |
+ } |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 108 | 120 |
// MaxConcurrentStreams returns a ServerOption that will apply a limit on the number |
| 109 | 121 |
// of concurrent streams to each ServerTransport. |
| 110 | 122 |
func MaxConcurrentStreams(n uint32) ServerOption {
|
| ... | ... |
@@ -120,6 +142,29 @@ func Creds(c credentials.Credentials) ServerOption {
|
| 120 | 120 |
} |
| 121 | 121 |
} |
| 122 | 122 |
|
| 123 |
+// UnaryInterceptor returns a ServerOption that sets the UnaryServerInterceptor for the |
|
| 124 |
+// server. Only one unary interceptor can be installed. The construction of multiple |
|
| 125 |
+// interceptors (e.g., chaining) can be implemented at the caller. |
|
| 126 |
+func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {
|
|
| 127 |
+ return func(o *options) {
|
|
| 128 |
+ if o.unaryInt != nil {
|
|
| 129 |
+ panic("The unary server interceptor has been set.")
|
|
| 130 |
+ } |
|
| 131 |
+ o.unaryInt = i |
|
| 132 |
+ } |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// StreamInterceptor returns a ServerOption that sets the StreamServerInterceptor for the |
|
| 136 |
+// server. Only one stream interceptor can be installed. |
|
| 137 |
+func StreamInterceptor(i StreamServerInterceptor) ServerOption {
|
|
| 138 |
+ return func(o *options) {
|
|
| 139 |
+ if o.streamInt != nil {
|
|
| 140 |
+ panic("The stream server interceptor has been set.")
|
|
| 141 |
+ } |
|
| 142 |
+ o.streamInt = i |
|
| 143 |
+ } |
|
| 144 |
+} |
|
| 145 |
+ |
|
| 123 | 146 |
// NewServer creates a gRPC server which has no service registered and has not |
| 124 | 147 |
// started to accept requests yet. |
| 125 | 148 |
func NewServer(opt ...ServerOption) *Server {
|
| ... | ... |
@@ -134,7 +179,7 @@ func NewServer(opt ...ServerOption) *Server {
|
| 134 | 134 |
s := &Server{
|
| 135 | 135 |
lis: make(map[net.Listener]bool), |
| 136 | 136 |
opts: opts, |
| 137 |
- conns: make(map[transport.ServerTransport]bool), |
|
| 137 |
+ conns: make(map[io.Closer]bool), |
|
| 138 | 138 |
m: make(map[string]*service), |
| 139 | 139 |
} |
| 140 | 140 |
if EnableTracing {
|
| ... | ... |
@@ -201,9 +246,17 @@ var ( |
| 201 | 201 |
ErrServerStopped = errors.New("grpc: the server has been stopped")
|
| 202 | 202 |
) |
| 203 | 203 |
|
| 204 |
+func (s *Server) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
|
| 205 |
+ creds, ok := s.opts.creds.(credentials.TransportAuthenticator) |
|
| 206 |
+ if !ok {
|
|
| 207 |
+ return rawConn, nil, nil |
|
| 208 |
+ } |
|
| 209 |
+ return creds.ServerHandshake(rawConn) |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 204 | 212 |
// Serve accepts incoming connections on the listener lis, creating a new |
| 205 | 213 |
// ServerTransport and service goroutine for each. The service goroutines |
| 206 |
-// read gRPC request and then call the registered handlers to reply to them. |
|
| 214 |
+// read gRPC requests and then call the registered handlers to reply to them. |
|
| 207 | 215 |
// Service returns when lis.Accept fails. |
| 208 | 216 |
func (s *Server) Serve(lis net.Listener) error {
|
| 209 | 217 |
s.mu.Lock() |
| ... | ... |
@@ -221,74 +274,167 @@ func (s *Server) Serve(lis net.Listener) error {
|
| 221 | 221 |
s.mu.Unlock() |
| 222 | 222 |
}() |
| 223 | 223 |
for {
|
| 224 |
- c, err := lis.Accept() |
|
| 224 |
+ rawConn, err := lis.Accept() |
|
| 225 | 225 |
if err != nil {
|
| 226 | 226 |
s.mu.Lock() |
| 227 | 227 |
s.printf("done serving; Accept = %v", err)
|
| 228 | 228 |
s.mu.Unlock() |
| 229 | 229 |
return err |
| 230 | 230 |
} |
| 231 |
- var authInfo credentials.AuthInfo |
|
| 232 |
- if creds, ok := s.opts.creds.(credentials.TransportAuthenticator); ok {
|
|
| 233 |
- var conn net.Conn |
|
| 234 |
- conn, authInfo, err = creds.ServerHandshake(c) |
|
| 235 |
- if err != nil {
|
|
| 236 |
- s.mu.Lock() |
|
| 237 |
- s.errorf("ServerHandshake(%q) failed: %v", c.RemoteAddr(), err)
|
|
| 238 |
- s.mu.Unlock() |
|
| 239 |
- grpclog.Println("grpc: Server.Serve failed to complete security handshake.")
|
|
| 240 |
- continue |
|
| 241 |
- } |
|
| 242 |
- c = conn |
|
| 243 |
- } |
|
| 231 |
+ // Start a new goroutine to deal with rawConn |
|
| 232 |
+ // so we don't stall this Accept loop goroutine. |
|
| 233 |
+ go s.handleRawConn(rawConn) |
|
| 234 |
+ } |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// handleRawConn is run in its own goroutine and handles a just-accepted |
|
| 238 |
+// connection that has not had any I/O performed on it yet. |
|
| 239 |
+func (s *Server) handleRawConn(rawConn net.Conn) {
|
|
| 240 |
+ conn, authInfo, err := s.useTransportAuthenticator(rawConn) |
|
| 241 |
+ if err != nil {
|
|
| 244 | 242 |
s.mu.Lock() |
| 245 |
- if s.conns == nil {
|
|
| 246 |
- s.mu.Unlock() |
|
| 247 |
- c.Close() |
|
| 248 |
- return nil |
|
| 249 |
- } |
|
| 250 |
- st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
|
|
| 251 |
- if err != nil {
|
|
| 252 |
- s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
|
| 253 |
- s.mu.Unlock() |
|
| 254 |
- c.Close() |
|
| 255 |
- grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
|
|
| 256 |
- continue |
|
| 257 |
- } |
|
| 258 |
- s.conns[st] = true |
|
| 243 |
+ s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
|
|
| 259 | 244 |
s.mu.Unlock() |
| 245 |
+ grpclog.Printf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
|
|
| 246 |
+ rawConn.Close() |
|
| 247 |
+ return |
|
| 248 |
+ } |
|
| 249 |
+ |
|
| 250 |
+ s.mu.Lock() |
|
| 251 |
+ if s.conns == nil {
|
|
| 252 |
+ s.mu.Unlock() |
|
| 253 |
+ conn.Close() |
|
| 254 |
+ return |
|
| 255 |
+ } |
|
| 256 |
+ s.mu.Unlock() |
|
| 257 |
+ |
|
| 258 |
+ if s.opts.useHandlerImpl {
|
|
| 259 |
+ s.serveUsingHandler(conn) |
|
| 260 |
+ } else {
|
|
| 261 |
+ s.serveNewHTTP2Transport(conn, authInfo) |
|
| 262 |
+ } |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+// serveNewHTTP2Transport sets up a new http/2 transport (using the |
|
| 266 |
+// gRPC http2 server transport in transport/http2_server.go) and |
|
| 267 |
+// serves streams on it. |
|
| 268 |
+// This is run in its own goroutine (it does network I/O in |
|
| 269 |
+// transport.NewServerTransport). |
|
| 270 |
+func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
|
| 271 |
+ st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
|
|
| 272 |
+ if err != nil {
|
|
| 273 |
+ s.mu.Lock() |
|
| 274 |
+ s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
|
| 275 |
+ s.mu.Unlock() |
|
| 276 |
+ c.Close() |
|
| 277 |
+ grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
|
|
| 278 |
+ return |
|
| 279 |
+ } |
|
| 280 |
+ if !s.addConn(st) {
|
|
| 281 |
+ st.Close() |
|
| 282 |
+ return |
|
| 283 |
+ } |
|
| 284 |
+ s.serveStreams(st) |
|
| 285 |
+} |
|
| 260 | 286 |
|
| 287 |
+func (s *Server) serveStreams(st transport.ServerTransport) {
|
|
| 288 |
+ defer s.removeConn(st) |
|
| 289 |
+ defer st.Close() |
|
| 290 |
+ var wg sync.WaitGroup |
|
| 291 |
+ st.HandleStreams(func(stream *transport.Stream) {
|
|
| 292 |
+ wg.Add(1) |
|
| 261 | 293 |
go func() {
|
| 262 |
- var wg sync.WaitGroup |
|
| 263 |
- st.HandleStreams(func(stream *transport.Stream) {
|
|
| 264 |
- var trInfo *traceInfo |
|
| 265 |
- if EnableTracing {
|
|
| 266 |
- trInfo = &traceInfo{
|
|
| 267 |
- tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
|
|
| 268 |
- } |
|
| 269 |
- trInfo.firstLine.client = false |
|
| 270 |
- trInfo.firstLine.remoteAddr = st.RemoteAddr() |
|
| 271 |
- stream.TraceContext(trInfo.tr) |
|
| 272 |
- if dl, ok := stream.Context().Deadline(); ok {
|
|
| 273 |
- trInfo.firstLine.deadline = dl.Sub(time.Now()) |
|
| 274 |
- } |
|
| 275 |
- } |
|
| 276 |
- wg.Add(1) |
|
| 277 |
- go func() {
|
|
| 278 |
- s.handleStream(st, stream, trInfo) |
|
| 279 |
- wg.Done() |
|
| 280 |
- }() |
|
| 281 |
- }) |
|
| 282 |
- wg.Wait() |
|
| 283 |
- s.mu.Lock() |
|
| 284 |
- delete(s.conns, st) |
|
| 285 |
- s.mu.Unlock() |
|
| 294 |
+ defer wg.Done() |
|
| 295 |
+ s.handleStream(st, stream, s.traceInfo(st, stream)) |
|
| 286 | 296 |
}() |
| 297 |
+ }) |
|
| 298 |
+ wg.Wait() |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+var _ http.Handler = (*Server)(nil) |
|
| 302 |
+ |
|
| 303 |
+// serveUsingHandler is called from handleRawConn when s is configured |
|
| 304 |
+// to handle requests via the http.Handler interface. It sets up a |
|
| 305 |
+// net/http.Server to handle the just-accepted conn. The http.Server |
|
| 306 |
+// is configured to route all incoming requests (all HTTP/2 streams) |
|
| 307 |
+// to ServeHTTP, which creates a new ServerTransport for each stream. |
|
| 308 |
+// serveUsingHandler blocks until conn closes. |
|
| 309 |
+// |
|
| 310 |
+// This codepath is only used when Server.TestingUseHandlerImpl has |
|
| 311 |
+// been configured. This lets the end2end tests exercise the ServeHTTP |
|
| 312 |
+// method as one of the environment types. |
|
| 313 |
+// |
|
| 314 |
+// conn is the *tls.Conn that's already been authenticated. |
|
| 315 |
+func (s *Server) serveUsingHandler(conn net.Conn) {
|
|
| 316 |
+ if !s.addConn(conn) {
|
|
| 317 |
+ conn.Close() |
|
| 318 |
+ return |
|
| 319 |
+ } |
|
| 320 |
+ defer s.removeConn(conn) |
|
| 321 |
+ h2s := &http2.Server{
|
|
| 322 |
+ MaxConcurrentStreams: s.opts.maxConcurrentStreams, |
|
| 323 |
+ } |
|
| 324 |
+ h2s.ServeConn(conn, &http2.ServeConnOpts{
|
|
| 325 |
+ Handler: s, |
|
| 326 |
+ }) |
|
| 327 |
+} |
|
| 328 |
+ |
|
| 329 |
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
| 330 |
+ st, err := transport.NewServerHandlerTransport(w, r) |
|
| 331 |
+ if err != nil {
|
|
| 332 |
+ http.Error(w, err.Error(), http.StatusInternalServerError) |
|
| 333 |
+ return |
|
| 334 |
+ } |
|
| 335 |
+ if !s.addConn(st) {
|
|
| 336 |
+ st.Close() |
|
| 337 |
+ return |
|
| 338 |
+ } |
|
| 339 |
+ defer s.removeConn(st) |
|
| 340 |
+ s.serveStreams(st) |
|
| 341 |
+} |
|
| 342 |
+ |
|
| 343 |
+// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled. |
|
| 344 |
+// If tracing is not enabled, it returns nil. |
|
| 345 |
+func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
|
| 346 |
+ if !EnableTracing {
|
|
| 347 |
+ return nil |
|
| 348 |
+ } |
|
| 349 |
+ trInfo = &traceInfo{
|
|
| 350 |
+ tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
|
|
| 351 |
+ } |
|
| 352 |
+ trInfo.firstLine.client = false |
|
| 353 |
+ trInfo.firstLine.remoteAddr = st.RemoteAddr() |
|
| 354 |
+ stream.TraceContext(trInfo.tr) |
|
| 355 |
+ if dl, ok := stream.Context().Deadline(); ok {
|
|
| 356 |
+ trInfo.firstLine.deadline = dl.Sub(time.Now()) |
|
| 357 |
+ } |
|
| 358 |
+ return trInfo |
|
| 359 |
+} |
|
| 360 |
+ |
|
| 361 |
+func (s *Server) addConn(c io.Closer) bool {
|
|
| 362 |
+ s.mu.Lock() |
|
| 363 |
+ defer s.mu.Unlock() |
|
| 364 |
+ if s.conns == nil {
|
|
| 365 |
+ return false |
|
| 366 |
+ } |
|
| 367 |
+ s.conns[c] = true |
|
| 368 |
+ return true |
|
| 369 |
+} |
|
| 370 |
+ |
|
| 371 |
+func (s *Server) removeConn(c io.Closer) {
|
|
| 372 |
+ s.mu.Lock() |
|
| 373 |
+ defer s.mu.Unlock() |
|
| 374 |
+ if s.conns != nil {
|
|
| 375 |
+ delete(s.conns, c) |
|
| 287 | 376 |
} |
| 288 | 377 |
} |
| 289 | 378 |
|
| 290 |
-func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, pf payloadFormat, opts *transport.Options) error {
|
|
| 291 |
- p, err := encode(s.opts.codec, msg, pf) |
|
| 379 |
+func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
|
|
| 380 |
+ var cbuf *bytes.Buffer |
|
| 381 |
+ if cp != nil {
|
|
| 382 |
+ cbuf = new(bytes.Buffer) |
|
| 383 |
+ } |
|
| 384 |
+ p, err := encode(s.opts.codec, msg, cp, cbuf) |
|
| 292 | 385 |
if err != nil {
|
| 293 | 386 |
// This typically indicates a fatal issue (e.g., memory |
| 294 | 387 |
// corruption or hardware faults) the application program |
| ... | ... |
@@ -314,97 +460,130 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. |
| 314 | 314 |
} |
| 315 | 315 |
}() |
| 316 | 316 |
} |
| 317 |
- p := &parser{s: stream}
|
|
| 317 |
+ p := &parser{r: stream}
|
|
| 318 | 318 |
for {
|
| 319 | 319 |
pf, req, err := p.recvMsg() |
| 320 | 320 |
if err == io.EOF {
|
| 321 | 321 |
// The entire stream is done (for unary RPC only). |
| 322 | 322 |
return err |
| 323 | 323 |
} |
| 324 |
+ if err == io.ErrUnexpectedEOF {
|
|
| 325 |
+ err = transport.StreamError{Code: codes.Internal, Desc: "io.ErrUnexpectedEOF"}
|
|
| 326 |
+ } |
|
| 324 | 327 |
if err != nil {
|
| 325 | 328 |
switch err := err.(type) {
|
| 326 | 329 |
case transport.ConnectionError: |
| 327 | 330 |
// Nothing to do here. |
| 328 | 331 |
case transport.StreamError: |
| 329 | 332 |
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
|
| 330 |
- grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
|
| 333 |
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
| 331 | 334 |
} |
| 332 | 335 |
default: |
| 333 | 336 |
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
|
| 334 | 337 |
} |
| 335 | 338 |
return err |
| 336 | 339 |
} |
| 337 |
- switch pf {
|
|
| 338 |
- case compressionNone: |
|
| 339 |
- statusCode := codes.OK |
|
| 340 |
- statusDesc := "" |
|
| 341 |
- df := func(v interface{}) error {
|
|
| 342 |
- if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
|
| 343 |
- return err |
|
| 344 |
- } |
|
| 345 |
- if trInfo != nil {
|
|
| 346 |
- trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
|
|
| 347 |
- } |
|
| 348 |
- return nil |
|
| 349 |
- } |
|
| 350 |
- reply, appErr := md.Handler(srv.server, stream.Context(), df) |
|
| 351 |
- if appErr != nil {
|
|
| 352 |
- if err, ok := appErr.(rpcError); ok {
|
|
| 353 |
- statusCode = err.code |
|
| 354 |
- statusDesc = err.desc |
|
| 355 |
- } else {
|
|
| 356 |
- statusCode = convertCode(appErr) |
|
| 357 |
- statusDesc = appErr.Error() |
|
| 340 |
+ |
|
| 341 |
+ if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
|
| 342 |
+ switch err := err.(type) {
|
|
| 343 |
+ case transport.StreamError: |
|
| 344 |
+ if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
|
|
| 345 |
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
| 358 | 346 |
} |
| 359 |
- if trInfo != nil && statusCode != codes.OK {
|
|
| 360 |
- trInfo.tr.LazyLog(stringer(statusDesc), true) |
|
| 361 |
- trInfo.tr.SetError() |
|
| 347 |
+ default: |
|
| 348 |
+ if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
|
| 349 |
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
| 362 | 350 |
} |
| 363 | 351 |
|
| 364 |
- if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
|
|
| 365 |
- grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
|
| 352 |
+ } |
|
| 353 |
+ return err |
|
| 354 |
+ } |
|
| 355 |
+ statusCode := codes.OK |
|
| 356 |
+ statusDesc := "" |
|
| 357 |
+ df := func(v interface{}) error {
|
|
| 358 |
+ if pf == compressionMade {
|
|
| 359 |
+ var err error |
|
| 360 |
+ req, err = s.opts.dc.Do(bytes.NewReader(req)) |
|
| 361 |
+ if err != nil {
|
|
| 362 |
+ if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
|
| 363 |
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
| 364 |
+ } |
|
| 366 | 365 |
return err |
| 367 | 366 |
} |
| 368 |
- return nil |
|
| 367 |
+ } |
|
| 368 |
+ if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
|
| 369 |
+ return err |
|
| 369 | 370 |
} |
| 370 | 371 |
if trInfo != nil {
|
| 371 |
- trInfo.tr.LazyLog(stringer("OK"), false)
|
|
| 372 |
+ trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
|
|
| 372 | 373 |
} |
| 373 |
- opts := &transport.Options{
|
|
| 374 |
- Last: true, |
|
| 375 |
- Delay: false, |
|
| 374 |
+ return nil |
|
| 375 |
+ } |
|
| 376 |
+ reply, appErr := md.Handler(srv.server, stream.Context(), df, s.opts.unaryInt) |
|
| 377 |
+ if appErr != nil {
|
|
| 378 |
+ if err, ok := appErr.(rpcError); ok {
|
|
| 379 |
+ statusCode = err.code |
|
| 380 |
+ statusDesc = err.desc |
|
| 381 |
+ } else {
|
|
| 382 |
+ statusCode = convertCode(appErr) |
|
| 383 |
+ statusDesc = appErr.Error() |
|
| 376 | 384 |
} |
| 377 |
- if err := s.sendResponse(t, stream, reply, compressionNone, opts); err != nil {
|
|
| 378 |
- switch err := err.(type) {
|
|
| 379 |
- case transport.ConnectionError: |
|
| 380 |
- // Nothing to do here. |
|
| 381 |
- case transport.StreamError: |
|
| 382 |
- statusCode = err.Code |
|
| 383 |
- statusDesc = err.Desc |
|
| 384 |
- default: |
|
| 385 |
- statusCode = codes.Unknown |
|
| 386 |
- statusDesc = err.Error() |
|
| 387 |
- } |
|
| 385 |
+ if trInfo != nil && statusCode != codes.OK {
|
|
| 386 |
+ trInfo.tr.LazyLog(stringer(statusDesc), true) |
|
| 387 |
+ trInfo.tr.SetError() |
|
| 388 |
+ } |
|
| 389 |
+ if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
|
|
| 390 |
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
|
| 388 | 391 |
return err |
| 389 | 392 |
} |
| 390 |
- if trInfo != nil {
|
|
| 391 |
- trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
|
| 393 |
+ return nil |
|
| 394 |
+ } |
|
| 395 |
+ if trInfo != nil {
|
|
| 396 |
+ trInfo.tr.LazyLog(stringer("OK"), false)
|
|
| 397 |
+ } |
|
| 398 |
+ opts := &transport.Options{
|
|
| 399 |
+ Last: true, |
|
| 400 |
+ Delay: false, |
|
| 401 |
+ } |
|
| 402 |
+ if s.opts.cp != nil {
|
|
| 403 |
+ stream.SetSendCompress(s.opts.cp.Type()) |
|
| 404 |
+ } |
|
| 405 |
+ if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
|
|
| 406 |
+ switch err := err.(type) {
|
|
| 407 |
+ case transport.ConnectionError: |
|
| 408 |
+ // Nothing to do here. |
|
| 409 |
+ case transport.StreamError: |
|
| 410 |
+ statusCode = err.Code |
|
| 411 |
+ statusDesc = err.Desc |
|
| 412 |
+ default: |
|
| 413 |
+ statusCode = codes.Unknown |
|
| 414 |
+ statusDesc = err.Error() |
|
| 392 | 415 |
} |
| 393 |
- return t.WriteStatus(stream, statusCode, statusDesc) |
|
| 394 |
- default: |
|
| 395 |
- panic(fmt.Sprintf("payload format to be supported: %d", pf))
|
|
| 416 |
+ return err |
|
| 396 | 417 |
} |
| 418 |
+ if trInfo != nil {
|
|
| 419 |
+ trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
|
| 420 |
+ } |
|
| 421 |
+ return t.WriteStatus(stream, statusCode, statusDesc) |
|
| 397 | 422 |
} |
| 398 | 423 |
} |
| 399 | 424 |
|
| 400 | 425 |
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
| 426 |
+ if s.opts.cp != nil {
|
|
| 427 |
+ stream.SetSendCompress(s.opts.cp.Type()) |
|
| 428 |
+ } |
|
| 401 | 429 |
ss := &serverStream{
|
| 402 | 430 |
t: t, |
| 403 | 431 |
s: stream, |
| 404 |
- p: &parser{s: stream},
|
|
| 432 |
+ p: &parser{r: stream},
|
|
| 405 | 433 |
codec: s.opts.codec, |
| 434 |
+ cp: s.opts.cp, |
|
| 435 |
+ dc: s.opts.dc, |
|
| 406 | 436 |
trInfo: trInfo, |
| 407 | 437 |
} |
| 438 |
+ if ss.cp != nil {
|
|
| 439 |
+ ss.cbuf = new(bytes.Buffer) |
|
| 440 |
+ } |
|
| 408 | 441 |
if trInfo != nil {
|
| 409 | 442 |
trInfo.tr.LazyLog(&trInfo.firstLine, false) |
| 410 | 443 |
defer func() {
|
| ... | ... |
@@ -418,10 +597,24 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp |
| 418 | 418 |
ss.mu.Unlock() |
| 419 | 419 |
}() |
| 420 | 420 |
} |
| 421 |
- if appErr := sd.Handler(srv.server, ss); appErr != nil {
|
|
| 421 |
+ var appErr error |
|
| 422 |
+ if s.opts.streamInt == nil {
|
|
| 423 |
+ appErr = sd.Handler(srv.server, ss) |
|
| 424 |
+ } else {
|
|
| 425 |
+ info := &StreamServerInfo{
|
|
| 426 |
+ FullMethod: stream.Method(), |
|
| 427 |
+ IsClientStream: sd.ClientStreams, |
|
| 428 |
+ IsServerStream: sd.ServerStreams, |
|
| 429 |
+ } |
|
| 430 |
+ appErr = s.opts.streamInt(srv.server, ss, info, sd.Handler) |
|
| 431 |
+ } |
|
| 432 |
+ if appErr != nil {
|
|
| 422 | 433 |
if err, ok := appErr.(rpcError); ok {
|
| 423 | 434 |
ss.statusCode = err.code |
| 424 | 435 |
ss.statusDesc = err.desc |
| 436 |
+ } else if err, ok := appErr.(transport.StreamError); ok {
|
|
| 437 |
+ ss.statusCode = err.Code |
|
| 438 |
+ ss.statusDesc = err.Desc |
|
| 425 | 439 |
} else {
|
| 426 | 440 |
ss.statusCode = convertCode(appErr) |
| 427 | 441 |
ss.statusDesc = appErr.Error() |
| ... | ... |
@@ -509,8 +702,11 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str |
| 509 | 509 |
} |
| 510 | 510 |
} |
| 511 | 511 |
|
| 512 |
-// Stop stops the gRPC server. Once Stop returns, the server stops accepting |
|
| 513 |
-// connection requests and closes all the connected connections. |
|
| 512 |
+// Stop stops the gRPC server. It immediately closes all open |
|
| 513 |
+// connections and listeners. |
|
| 514 |
+// It cancels all active RPCs on the server side and the corresponding |
|
| 515 |
+// pending RPCs on the client side will get notified by connection |
|
| 516 |
+// errors. |
|
| 514 | 517 |
func (s *Server) Stop() {
|
| 515 | 518 |
s.mu.Lock() |
| 516 | 519 |
listeners := s.lis |
| ... | ... |
@@ -518,12 +714,14 @@ func (s *Server) Stop() {
|
| 518 | 518 |
cs := s.conns |
| 519 | 519 |
s.conns = nil |
| 520 | 520 |
s.mu.Unlock() |
| 521 |
+ |
|
| 521 | 522 |
for lis := range listeners {
|
| 522 | 523 |
lis.Close() |
| 523 | 524 |
} |
| 524 | 525 |
for c := range cs {
|
| 525 | 526 |
c.Close() |
| 526 | 527 |
} |
| 528 |
+ |
|
| 527 | 529 |
s.mu.Lock() |
| 528 | 530 |
if s.events != nil {
|
| 529 | 531 |
s.events.Finish() |
| ... | ... |
@@ -532,14 +730,23 @@ func (s *Server) Stop() {
|
| 532 | 532 |
s.mu.Unlock() |
| 533 | 533 |
} |
| 534 | 534 |
|
| 535 |
-// TestingCloseConns closes all exiting transports but keeps s.lis accepting new |
|
| 536 |
-// connections. This is for test only now. |
|
| 537 |
-func (s *Server) TestingCloseConns() {
|
|
| 535 |
+func init() {
|
|
| 536 |
+ internal.TestingCloseConns = func(arg interface{}) {
|
|
| 537 |
+ arg.(*Server).testingCloseConns() |
|
| 538 |
+ } |
|
| 539 |
+ internal.TestingUseHandlerImpl = func(arg interface{}) {
|
|
| 540 |
+ arg.(*Server).opts.useHandlerImpl = true |
|
| 541 |
+ } |
|
| 542 |
+} |
|
| 543 |
+ |
|
| 544 |
+// testingCloseConns closes all existing transports but keeps s.lis |
|
| 545 |
+// accepting new connections. |
|
| 546 |
+func (s *Server) testingCloseConns() {
|
|
| 538 | 547 |
s.mu.Lock() |
| 539 | 548 |
for c := range s.conns {
|
| 540 | 549 |
c.Close() |
| 550 |
+ delete(s.conns, c) |
|
| 541 | 551 |
} |
| 542 |
- s.conns = make(map[transport.ServerTransport]bool) |
|
| 543 | 552 |
s.mu.Unlock() |
| 544 | 553 |
} |
| 545 | 554 |
|
| ... | ... |
@@ -34,6 +34,7 @@ |
| 34 | 34 |
package grpc |
| 35 | 35 |
|
| 36 | 36 |
import ( |
| 37 |
+ "bytes" |
|
| 37 | 38 |
"errors" |
| 38 | 39 |
"io" |
| 39 | 40 |
"sync" |
| ... | ... |
@@ -46,12 +47,14 @@ import ( |
| 46 | 46 |
"google.golang.org/grpc/transport" |
| 47 | 47 |
) |
| 48 | 48 |
|
| 49 |
-type streamHandler func(srv interface{}, stream ServerStream) error
|
|
| 49 |
+// StreamHandler defines the handler called by gRPC server to complete the |
|
| 50 |
+// execution of a streaming RPC. |
|
| 51 |
+type StreamHandler func(srv interface{}, stream ServerStream) error
|
|
| 50 | 52 |
|
| 51 | 53 |
// StreamDesc represents a streaming RPC service's method specification. |
| 52 | 54 |
type StreamDesc struct {
|
| 53 | 55 |
StreamName string |
| 54 |
- Handler streamHandler |
|
| 56 |
+ Handler StreamHandler |
|
| 55 | 57 |
|
| 56 | 58 |
// At least one of these is true. |
| 57 | 59 |
ServerStreams bool |
| ... | ... |
@@ -66,18 +69,19 @@ type Stream interface {
|
| 66 | 66 |
// breaks. |
| 67 | 67 |
// On error, it aborts the stream and returns an RPC status on client |
| 68 | 68 |
// side. On server side, it simply returns the error to the caller. |
| 69 |
- // SendMsg is called by generated code. |
|
| 69 |
+ // SendMsg is called by generated code. Also Users can call SendMsg |
|
| 70 |
+ // directly when it is really needed in their use cases. |
|
| 70 | 71 |
SendMsg(m interface{}) error
|
| 71 | 72 |
// RecvMsg blocks until it receives a message or the stream is |
| 72 | 73 |
// done. On client side, it returns io.EOF when the stream is done. On |
| 73 |
- // any other error, it aborts the streama nd returns an RPC status. On |
|
| 74 |
+ // any other error, it aborts the stream and returns an RPC status. On |
|
| 74 | 75 |
// server side, it simply returns the error to the caller. |
| 75 | 76 |
RecvMsg(m interface{}) error
|
| 76 | 77 |
} |
| 77 | 78 |
|
| 78 | 79 |
// ClientStream defines the interface a client stream has to satify. |
| 79 | 80 |
type ClientStream interface {
|
| 80 |
- // Header returns the header metedata received from the server if there |
|
| 81 |
+ // Header returns the header metadata received from the server if there |
|
| 81 | 82 |
// is any. It blocks if the metadata is not ready to read. |
| 82 | 83 |
Header() (metadata.MD, error) |
| 83 | 84 |
// Trailer returns the trailer metadata from the server. It must be called |
| ... | ... |
@@ -108,12 +112,22 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth |
| 108 | 108 |
callHdr := &transport.CallHdr{
|
| 109 | 109 |
Host: cc.authority, |
| 110 | 110 |
Method: method, |
| 111 |
+ Flush: desc.ServerStreams && desc.ClientStreams, |
|
| 112 |
+ } |
|
| 113 |
+ if cc.dopts.cp != nil {
|
|
| 114 |
+ callHdr.SendCompress = cc.dopts.cp.Type() |
|
| 111 | 115 |
} |
| 112 | 116 |
cs := &clientStream{
|
| 113 | 117 |
desc: desc, |
| 114 | 118 |
codec: cc.dopts.codec, |
| 119 |
+ cp: cc.dopts.cp, |
|
| 120 |
+ dc: cc.dopts.dc, |
|
| 115 | 121 |
tracing: EnableTracing, |
| 116 | 122 |
} |
| 123 |
+ if cc.dopts.cp != nil {
|
|
| 124 |
+ callHdr.SendCompress = cc.dopts.cp.Type() |
|
| 125 |
+ cs.cbuf = new(bytes.Buffer) |
|
| 126 |
+ } |
|
| 117 | 127 |
if cs.tracing {
|
| 118 | 128 |
cs.trInfo.tr = trace.New("grpc.Sent."+methodFamily(method), method)
|
| 119 | 129 |
cs.trInfo.firstLine.client = true |
| ... | ... |
@@ -125,16 +139,23 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth |
| 125 | 125 |
} |
| 126 | 126 |
s, err := t.NewStream(ctx, callHdr) |
| 127 | 127 |
if err != nil {
|
| 128 |
+ cs.finish(err) |
|
| 128 | 129 |
return nil, toRPCErr(err) |
| 129 | 130 |
} |
| 130 | 131 |
cs.t = t |
| 131 | 132 |
cs.s = s |
| 132 |
- cs.p = &parser{s: s}
|
|
| 133 |
+ cs.p = &parser{r: s}
|
|
| 133 | 134 |
// Listen on ctx.Done() to detect cancellation when there is no pending |
| 134 | 135 |
// I/O operations on this stream. |
| 135 | 136 |
go func() {
|
| 136 |
- <-s.Context().Done() |
|
| 137 |
- cs.closeTransportStream(transport.ContextErr(s.Context().Err())) |
|
| 137 |
+ select {
|
|
| 138 |
+ case <-t.Error(): |
|
| 139 |
+ // Incur transport error, simply exit. |
|
| 140 |
+ case <-s.Context().Done(): |
|
| 141 |
+ err := s.Context().Err() |
|
| 142 |
+ cs.finish(err) |
|
| 143 |
+ cs.closeTransportStream(transport.ContextErr(err)) |
|
| 144 |
+ } |
|
| 138 | 145 |
}() |
| 139 | 146 |
return cs, nil |
| 140 | 147 |
} |
| ... | ... |
@@ -146,6 +167,9 @@ type clientStream struct {
|
| 146 | 146 |
p *parser |
| 147 | 147 |
desc *StreamDesc |
| 148 | 148 |
codec Codec |
| 149 |
+ cp Compressor |
|
| 150 |
+ cbuf *bytes.Buffer |
|
| 151 |
+ dc Decompressor |
|
| 149 | 152 |
|
| 150 | 153 |
tracing bool // set to EnableTracing when the clientStream is created. |
| 151 | 154 |
|
| ... | ... |
@@ -183,6 +207,9 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
| 183 | 183 |
cs.mu.Unlock() |
| 184 | 184 |
} |
| 185 | 185 |
defer func() {
|
| 186 |
+ if err != nil {
|
|
| 187 |
+ cs.finish(err) |
|
| 188 |
+ } |
|
| 186 | 189 |
if err == nil || err == io.EOF {
|
| 187 | 190 |
return |
| 188 | 191 |
} |
| ... | ... |
@@ -191,7 +218,12 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
| 191 | 191 |
} |
| 192 | 192 |
err = toRPCErr(err) |
| 193 | 193 |
}() |
| 194 |
- out, err := encode(cs.codec, m, compressionNone) |
|
| 194 |
+ out, err := encode(cs.codec, m, cs.cp, cs.cbuf) |
|
| 195 |
+ defer func() {
|
|
| 196 |
+ if cs.cbuf != nil {
|
|
| 197 |
+ cs.cbuf.Reset() |
|
| 198 |
+ } |
|
| 199 |
+ }() |
|
| 195 | 200 |
if err != nil {
|
| 196 | 201 |
return transport.StreamErrorf(codes.Internal, "grpc: %v", err) |
| 197 | 202 |
} |
| ... | ... |
@@ -199,7 +231,7 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 | 201 |
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
| 202 |
- err = recv(cs.p, cs.codec, m) |
|
| 202 |
+ err = recv(cs.p, cs.codec, cs.s, cs.dc, m) |
|
| 203 | 203 |
defer func() {
|
| 204 | 204 |
// err != nil indicates the termination of the stream. |
| 205 | 205 |
if err != nil {
|
| ... | ... |
@@ -218,16 +250,17 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
| 218 | 218 |
return |
| 219 | 219 |
} |
| 220 | 220 |
// Special handling for client streaming rpc. |
| 221 |
- err = recv(cs.p, cs.codec, m) |
|
| 221 |
+ err = recv(cs.p, cs.codec, cs.s, cs.dc, m) |
|
| 222 | 222 |
cs.closeTransportStream(err) |
| 223 | 223 |
if err == nil {
|
| 224 | 224 |
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
|
| 225 | 225 |
} |
| 226 | 226 |
if err == io.EOF {
|
| 227 | 227 |
if cs.s.StatusCode() == codes.OK {
|
| 228 |
+ cs.finish(err) |
|
| 228 | 229 |
return nil |
| 229 | 230 |
} |
| 230 |
- return Errorf(cs.s.StatusCode(), cs.s.StatusDesc()) |
|
| 231 |
+ return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc()) |
|
| 231 | 232 |
} |
| 232 | 233 |
return toRPCErr(err) |
| 233 | 234 |
} |
| ... | ... |
@@ -239,13 +272,18 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
| 239 | 239 |
// Returns io.EOF to indicate the end of the stream. |
| 240 | 240 |
return |
| 241 | 241 |
} |
| 242 |
- return Errorf(cs.s.StatusCode(), cs.s.StatusDesc()) |
|
| 242 |
+ return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc()) |
|
| 243 | 243 |
} |
| 244 | 244 |
return toRPCErr(err) |
| 245 | 245 |
} |
| 246 | 246 |
|
| 247 | 247 |
func (cs *clientStream) CloseSend() (err error) {
|
| 248 | 248 |
err = cs.t.Write(cs.s, nil, &transport.Options{Last: true})
|
| 249 |
+ defer func() {
|
|
| 250 |
+ if err != nil {
|
|
| 251 |
+ cs.finish(err) |
|
| 252 |
+ } |
|
| 253 |
+ }() |
|
| 249 | 254 |
if err == nil || err == io.EOF {
|
| 250 | 255 |
return |
| 251 | 256 |
} |
| ... | ... |
@@ -303,6 +341,9 @@ type serverStream struct {
|
| 303 | 303 |
s *transport.Stream |
| 304 | 304 |
p *parser |
| 305 | 305 |
codec Codec |
| 306 |
+ cp Compressor |
|
| 307 |
+ dc Decompressor |
|
| 308 |
+ cbuf *bytes.Buffer |
|
| 306 | 309 |
statusCode codes.Code |
| 307 | 310 |
statusDesc string |
| 308 | 311 |
trInfo *traceInfo |
| ... | ... |
@@ -341,7 +382,12 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
|
| 341 | 341 |
ss.mu.Unlock() |
| 342 | 342 |
} |
| 343 | 343 |
}() |
| 344 |
- out, err := encode(ss.codec, m, compressionNone) |
|
| 344 |
+ out, err := encode(ss.codec, m, ss.cp, ss.cbuf) |
|
| 345 |
+ defer func() {
|
|
| 346 |
+ if ss.cbuf != nil {
|
|
| 347 |
+ ss.cbuf.Reset() |
|
| 348 |
+ } |
|
| 349 |
+ }() |
|
| 345 | 350 |
if err != nil {
|
| 346 | 351 |
err = transport.StreamErrorf(codes.Internal, "grpc: %v", err) |
| 347 | 352 |
return err |
| ... | ... |
@@ -364,5 +410,5 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
| 364 | 364 |
ss.mu.Unlock() |
| 365 | 365 |
} |
| 366 | 366 |
}() |
| 367 |
- return recv(ss.p, ss.codec, m) |
|
| 367 |
+ return recv(ss.p, ss.codec, ss.s, ss.dc, m) |
|
| 368 | 368 |
} |
| ... | ... |
@@ -56,43 +56,33 @@ type windowUpdate struct {
|
| 56 | 56 |
increment uint32 |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
-func (windowUpdate) isItem() bool {
|
|
| 60 |
- return true |
|
| 61 |
-} |
|
| 59 |
+func (*windowUpdate) item() {}
|
|
| 62 | 60 |
|
| 63 | 61 |
type settings struct {
|
| 64 | 62 |
ack bool |
| 65 | 63 |
ss []http2.Setting |
| 66 | 64 |
} |
| 67 | 65 |
|
| 68 |
-func (settings) isItem() bool {
|
|
| 69 |
- return true |
|
| 70 |
-} |
|
| 66 |
+func (*settings) item() {}
|
|
| 71 | 67 |
|
| 72 | 68 |
type resetStream struct {
|
| 73 | 69 |
streamID uint32 |
| 74 | 70 |
code http2.ErrCode |
| 75 | 71 |
} |
| 76 | 72 |
|
| 77 |
-func (resetStream) isItem() bool {
|
|
| 78 |
- return true |
|
| 79 |
-} |
|
| 73 |
+func (*resetStream) item() {}
|
|
| 80 | 74 |
|
| 81 | 75 |
type flushIO struct {
|
| 82 | 76 |
} |
| 83 | 77 |
|
| 84 |
-func (flushIO) isItem() bool {
|
|
| 85 |
- return true |
|
| 86 |
-} |
|
| 78 |
+func (*flushIO) item() {}
|
|
| 87 | 79 |
|
| 88 | 80 |
type ping struct {
|
| 89 | 81 |
ack bool |
| 90 | 82 |
data [8]byte |
| 91 | 83 |
} |
| 92 | 84 |
|
| 93 |
-func (ping) isItem() bool {
|
|
| 94 |
- return true |
|
| 95 |
-} |
|
| 85 |
+func (*ping) item() {}
|
|
| 96 | 86 |
|
| 97 | 87 |
// quotaPool is a pool which accumulates the quota and sends it to acquire() |
| 98 | 88 |
// when it is available. |
| ... | ... |
@@ -172,10 +162,6 @@ func (qb *quotaPool) acquire() <-chan int {
|
| 172 | 172 |
type inFlow struct {
|
| 173 | 173 |
// The inbound flow control limit for pending data. |
| 174 | 174 |
limit uint32 |
| 175 |
- // conn points to the shared connection-level inFlow that is shared |
|
| 176 |
- // by all streams on that conn. It is nil for the inFlow on the conn |
|
| 177 |
- // directly. |
|
| 178 |
- conn *inFlow |
|
| 179 | 175 |
|
| 180 | 176 |
mu sync.Mutex |
| 181 | 177 |
// pendingData is the overall data which have been received but not been |
| ... | ... |
@@ -186,75 +172,39 @@ type inFlow struct {
|
| 186 | 186 |
pendingUpdate uint32 |
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 |
-// onData is invoked when some data frame is received. It increments not only its |
|
| 190 |
-// own pendingData but also that of the associated connection-level flow. |
|
| 189 |
+// onData is invoked when some data frame is received. It updates pendingData. |
|
| 191 | 190 |
func (f *inFlow) onData(n uint32) error {
|
| 192 |
- if n == 0 {
|
|
| 193 |
- return nil |
|
| 194 |
- } |
|
| 195 | 191 |
f.mu.Lock() |
| 196 | 192 |
defer f.mu.Unlock() |
| 197 |
- if f.pendingData+f.pendingUpdate+n > f.limit {
|
|
| 198 |
- return fmt.Errorf("recieved %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate+n, f.limit)
|
|
| 199 |
- } |
|
| 200 |
- if f.conn != nil {
|
|
| 201 |
- if err := f.conn.onData(n); err != nil {
|
|
| 202 |
- return ConnectionErrorf("%v", err)
|
|
| 203 |
- } |
|
| 204 |
- } |
|
| 205 | 193 |
f.pendingData += n |
| 206 |
- return nil |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-// connOnRead updates the connection level states when the application consumes data. |
|
| 210 |
-func (f *inFlow) connOnRead(n uint32) uint32 {
|
|
| 211 |
- if n == 0 || f.conn != nil {
|
|
| 212 |
- return 0 |
|
| 213 |
- } |
|
| 214 |
- f.mu.Lock() |
|
| 215 |
- defer f.mu.Unlock() |
|
| 216 |
- f.pendingData -= n |
|
| 217 |
- f.pendingUpdate += n |
|
| 218 |
- if f.pendingUpdate >= f.limit/4 {
|
|
| 219 |
- ret := f.pendingUpdate |
|
| 220 |
- f.pendingUpdate = 0 |
|
| 221 |
- return ret |
|
| 194 |
+ if f.pendingData+f.pendingUpdate > f.limit {
|
|
| 195 |
+ return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate, f.limit)
|
|
| 222 | 196 |
} |
| 223 |
- return 0 |
|
| 197 |
+ return nil |
|
| 224 | 198 |
} |
| 225 | 199 |
|
| 226 |
-// onRead is invoked when the application reads the data. It returns the window updates |
|
| 227 |
-// for both stream and connection level. |
|
| 228 |
-func (f *inFlow) onRead(n uint32) (swu, cwu uint32) {
|
|
| 229 |
- if n == 0 {
|
|
| 230 |
- return |
|
| 231 |
- } |
|
| 200 |
+// onRead is invoked when the application reads the data. It returns the window size |
|
| 201 |
+// to be sent to the peer. |
|
| 202 |
+func (f *inFlow) onRead(n uint32) uint32 {
|
|
| 232 | 203 |
f.mu.Lock() |
| 233 | 204 |
defer f.mu.Unlock() |
| 234 | 205 |
if f.pendingData == 0 {
|
| 235 |
- // pendingData has been adjusted by restoreConn. |
|
| 236 |
- return |
|
| 206 |
+ return 0 |
|
| 237 | 207 |
} |
| 238 | 208 |
f.pendingData -= n |
| 239 | 209 |
f.pendingUpdate += n |
| 240 | 210 |
if f.pendingUpdate >= f.limit/4 {
|
| 241 |
- swu = f.pendingUpdate |
|
| 211 |
+ wu := f.pendingUpdate |
|
| 242 | 212 |
f.pendingUpdate = 0 |
| 213 |
+ return wu |
|
| 243 | 214 |
} |
| 244 |
- cwu = f.conn.connOnRead(n) |
|
| 245 |
- return |
|
| 215 |
+ return 0 |
|
| 246 | 216 |
} |
| 247 | 217 |
|
| 248 |
-// restoreConn is invoked when a stream is terminated. It removes its stake in |
|
| 249 |
-// the connection-level flow and resets its own state. |
|
| 250 |
-func (f *inFlow) restoreConn() uint32 {
|
|
| 251 |
- if f.conn == nil {
|
|
| 252 |
- return 0 |
|
| 253 |
- } |
|
| 218 |
+func (f *inFlow) resetPendingData() uint32 {
|
|
| 254 | 219 |
f.mu.Lock() |
| 255 | 220 |
defer f.mu.Unlock() |
| 256 | 221 |
n := f.pendingData |
| 257 | 222 |
f.pendingData = 0 |
| 258 |
- f.pendingUpdate = 0 |
|
| 259 |
- return f.conn.connOnRead(n) |
|
| 223 |
+ return n |
|
| 260 | 224 |
} |
| 261 | 225 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,383 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright 2016, Google Inc. |
|
| 2 |
+ * All rights reserved. |
|
| 3 |
+ * |
|
| 4 |
+ * Redistribution and use in source and binary forms, with or without |
|
| 5 |
+ * modification, are permitted provided that the following conditions are |
|
| 6 |
+ * met: |
|
| 7 |
+ * |
|
| 8 |
+ * * Redistributions of source code must retain the above copyright |
|
| 9 |
+ * notice, this list of conditions and the following disclaimer. |
|
| 10 |
+ * * Redistributions in binary form must reproduce the above |
|
| 11 |
+ * copyright notice, this list of conditions and the following disclaimer |
|
| 12 |
+ * in the documentation and/or other materials provided with the |
|
| 13 |
+ * distribution. |
|
| 14 |
+ * * Neither the name of Google Inc. nor the names of its |
|
| 15 |
+ * contributors may be used to endorse or promote products derived from |
|
| 16 |
+ * this software without specific prior written permission. |
|
| 17 |
+ * |
|
| 18 |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 19 |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 20 |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 21 |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 22 |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 23 |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 24 |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 25 |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 26 |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 27 |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 28 |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 29 |
+ * |
|
| 30 |
+ */ |
|
| 31 |
+ |
|
| 32 |
+// This file is the implementation of a gRPC server using HTTP/2 which |
|
| 33 |
+// uses the standard Go http2 Server implementation (via the |
|
| 34 |
+// http.Handler interface), rather than speaking low-level HTTP/2 |
|
| 35 |
+// frames itself. It is the implementation of *grpc.Server.ServeHTTP. |
|
| 36 |
+ |
|
| 37 |
+package transport |
|
| 38 |
+ |
|
| 39 |
+import ( |
|
| 40 |
+ "errors" |
|
| 41 |
+ "fmt" |
|
| 42 |
+ "io" |
|
| 43 |
+ "net" |
|
| 44 |
+ "net/http" |
|
| 45 |
+ "strings" |
|
| 46 |
+ "sync" |
|
| 47 |
+ "time" |
|
| 48 |
+ |
|
| 49 |
+ "golang.org/x/net/context" |
|
| 50 |
+ "golang.org/x/net/http2" |
|
| 51 |
+ "google.golang.org/grpc/codes" |
|
| 52 |
+ "google.golang.org/grpc/credentials" |
|
| 53 |
+ "google.golang.org/grpc/metadata" |
|
| 54 |
+ "google.golang.org/grpc/peer" |
|
| 55 |
+) |
|
| 56 |
+ |
|
| 57 |
+// NewServerHandlerTransport returns a ServerTransport handling gRPC |
|
| 58 |
+// from inside an http.Handler. It requires that the http Server |
|
| 59 |
+// supports HTTP/2. |
|
| 60 |
+func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request) (ServerTransport, error) {
|
|
| 61 |
+ if r.ProtoMajor != 2 {
|
|
| 62 |
+ return nil, errors.New("gRPC requires HTTP/2")
|
|
| 63 |
+ } |
|
| 64 |
+ if r.Method != "POST" {
|
|
| 65 |
+ return nil, errors.New("invalid gRPC request method")
|
|
| 66 |
+ } |
|
| 67 |
+ if !strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
|
|
| 68 |
+ return nil, errors.New("invalid gRPC request content-type")
|
|
| 69 |
+ } |
|
| 70 |
+ if _, ok := w.(http.Flusher); !ok {
|
|
| 71 |
+ return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher")
|
|
| 72 |
+ } |
|
| 73 |
+ if _, ok := w.(http.CloseNotifier); !ok {
|
|
| 74 |
+ return nil, errors.New("gRPC requires a ResponseWriter supporting http.CloseNotifier")
|
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ st := &serverHandlerTransport{
|
|
| 78 |
+ rw: w, |
|
| 79 |
+ req: r, |
|
| 80 |
+ closedCh: make(chan struct{}),
|
|
| 81 |
+ writes: make(chan func()), |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ if v := r.Header.Get("grpc-timeout"); v != "" {
|
|
| 85 |
+ to, err := timeoutDecode(v) |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return nil, StreamErrorf(codes.Internal, "malformed time-out: %v", err) |
|
| 88 |
+ } |
|
| 89 |
+ st.timeoutSet = true |
|
| 90 |
+ st.timeout = to |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ var metakv []string |
|
| 94 |
+ for k, vv := range r.Header {
|
|
| 95 |
+ k = strings.ToLower(k) |
|
| 96 |
+ if isReservedHeader(k) {
|
|
| 97 |
+ continue |
|
| 98 |
+ } |
|
| 99 |
+ for _, v := range vv {
|
|
| 100 |
+ if k == "user-agent" {
|
|
| 101 |
+ // user-agent is special. Copying logic of http_util.go. |
|
| 102 |
+ if i := strings.LastIndex(v, " "); i == -1 {
|
|
| 103 |
+ // There is no application user agent string being set |
|
| 104 |
+ continue |
|
| 105 |
+ } else {
|
|
| 106 |
+ v = v[:i] |
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ metakv = append(metakv, k, v) |
|
| 110 |
+ |
|
| 111 |
+ } |
|
| 112 |
+ } |
|
| 113 |
+ st.headerMD = metadata.Pairs(metakv...) |
|
| 114 |
+ |
|
| 115 |
+ return st, nil |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+// serverHandlerTransport is an implementation of ServerTransport |
|
| 119 |
+// which replies to exactly one gRPC request (exactly one HTTP request), |
|
| 120 |
+// using the net/http.Handler interface. This http.Handler is guaranteed |
|
| 121 |
+// at this point to be speaking over HTTP/2, so it's able to speak valid |
|
| 122 |
+// gRPC. |
|
| 123 |
+type serverHandlerTransport struct {
|
|
| 124 |
+ rw http.ResponseWriter |
|
| 125 |
+ req *http.Request |
|
| 126 |
+ timeoutSet bool |
|
| 127 |
+ timeout time.Duration |
|
| 128 |
+ didCommonHeaders bool |
|
| 129 |
+ |
|
| 130 |
+ headerMD metadata.MD |
|
| 131 |
+ |
|
| 132 |
+ closeOnce sync.Once |
|
| 133 |
+ closedCh chan struct{} // closed on Close
|
|
| 134 |
+ |
|
| 135 |
+ // writes is a channel of code to run serialized in the |
|
| 136 |
+ // ServeHTTP (HandleStreams) goroutine. The channel is closed |
|
| 137 |
+ // when WriteStatus is called. |
|
| 138 |
+ writes chan func() |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+func (ht *serverHandlerTransport) Close() error {
|
|
| 142 |
+ ht.closeOnce.Do(ht.closeCloseChanOnce) |
|
| 143 |
+ return nil |
|
| 144 |
+} |
|
| 145 |
+ |
|
| 146 |
+func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
|
|
| 147 |
+ |
|
| 148 |
+func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) }
|
|
| 149 |
+ |
|
| 150 |
+// strAddr is a net.Addr backed by either a TCP "ip:port" string, or |
|
| 151 |
+// the empty string if unknown. |
|
| 152 |
+type strAddr string |
|
| 153 |
+ |
|
| 154 |
+func (a strAddr) Network() string {
|
|
| 155 |
+ if a != "" {
|
|
| 156 |
+ // Per the documentation on net/http.Request.RemoteAddr, if this is |
|
| 157 |
+ // set, it's set to the IP:port of the peer (hence, TCP): |
|
| 158 |
+ // https://golang.org/pkg/net/http/#Request |
|
| 159 |
+ // |
|
| 160 |
+ // If we want to support Unix sockets later, we can |
|
| 161 |
+ // add our own grpc-specific convention within the |
|
| 162 |
+ // grpc codebase to set RemoteAddr to a different |
|
| 163 |
+ // format, or probably better: we can attach it to the |
|
| 164 |
+ // context and use that from serverHandlerTransport.RemoteAddr. |
|
| 165 |
+ return "tcp" |
|
| 166 |
+ } |
|
| 167 |
+ return "" |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func (a strAddr) String() string { return string(a) }
|
|
| 171 |
+ |
|
| 172 |
+// do runs fn in the ServeHTTP goroutine. |
|
| 173 |
+func (ht *serverHandlerTransport) do(fn func()) error {
|
|
| 174 |
+ select {
|
|
| 175 |
+ case ht.writes <- fn: |
|
| 176 |
+ return nil |
|
| 177 |
+ case <-ht.closedCh: |
|
| 178 |
+ return ErrConnClosing |
|
| 179 |
+ } |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
|
|
| 183 |
+ err := ht.do(func() {
|
|
| 184 |
+ ht.writeCommonHeaders(s) |
|
| 185 |
+ |
|
| 186 |
+ // And flush, in case no header or body has been sent yet. |
|
| 187 |
+ // This forces a separation of headers and trailers if this is the |
|
| 188 |
+ // first call (for example, in end2end tests's TestNoService). |
|
| 189 |
+ ht.rw.(http.Flusher).Flush() |
|
| 190 |
+ |
|
| 191 |
+ h := ht.rw.Header() |
|
| 192 |
+ h.Set("Grpc-Status", fmt.Sprintf("%d", statusCode))
|
|
| 193 |
+ if statusDesc != "" {
|
|
| 194 |
+ h.Set("Grpc-Message", statusDesc)
|
|
| 195 |
+ } |
|
| 196 |
+ if md := s.Trailer(); len(md) > 0 {
|
|
| 197 |
+ for k, vv := range md {
|
|
| 198 |
+ for _, v := range vv {
|
|
| 199 |
+ // http2 ResponseWriter mechanism to |
|
| 200 |
+ // send undeclared Trailers after the |
|
| 201 |
+ // headers have possibly been written. |
|
| 202 |
+ h.Add(http2.TrailerPrefix+k, v) |
|
| 203 |
+ } |
|
| 204 |
+ } |
|
| 205 |
+ } |
|
| 206 |
+ }) |
|
| 207 |
+ close(ht.writes) |
|
| 208 |
+ return err |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+// writeCommonHeaders sets common headers on the first write |
|
| 212 |
+// call (Write, WriteHeader, or WriteStatus). |
|
| 213 |
+func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
|
|
| 214 |
+ if ht.didCommonHeaders {
|
|
| 215 |
+ return |
|
| 216 |
+ } |
|
| 217 |
+ ht.didCommonHeaders = true |
|
| 218 |
+ |
|
| 219 |
+ h := ht.rw.Header() |
|
| 220 |
+ h["Date"] = nil // suppress Date to make tests happy; TODO: restore |
|
| 221 |
+ h.Set("Content-Type", "application/grpc")
|
|
| 222 |
+ |
|
| 223 |
+ // Predeclare trailers we'll set later in WriteStatus (after the body). |
|
| 224 |
+ // This is a SHOULD in the HTTP RFC, and the way you add (known) |
|
| 225 |
+ // Trailers per the net/http.ResponseWriter contract. |
|
| 226 |
+ // See https://golang.org/pkg/net/http/#ResponseWriter |
|
| 227 |
+ // and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers |
|
| 228 |
+ h.Add("Trailer", "Grpc-Status")
|
|
| 229 |
+ h.Add("Trailer", "Grpc-Message")
|
|
| 230 |
+ |
|
| 231 |
+ if s.sendCompress != "" {
|
|
| 232 |
+ h.Set("Grpc-Encoding", s.sendCompress)
|
|
| 233 |
+ } |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func (ht *serverHandlerTransport) Write(s *Stream, data []byte, opts *Options) error {
|
|
| 237 |
+ return ht.do(func() {
|
|
| 238 |
+ ht.writeCommonHeaders(s) |
|
| 239 |
+ ht.rw.Write(data) |
|
| 240 |
+ if !opts.Delay {
|
|
| 241 |
+ ht.rw.(http.Flusher).Flush() |
|
| 242 |
+ } |
|
| 243 |
+ }) |
|
| 244 |
+} |
|
| 245 |
+ |
|
| 246 |
+func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
|
|
| 247 |
+ return ht.do(func() {
|
|
| 248 |
+ ht.writeCommonHeaders(s) |
|
| 249 |
+ h := ht.rw.Header() |
|
| 250 |
+ for k, vv := range md {
|
|
| 251 |
+ for _, v := range vv {
|
|
| 252 |
+ h.Add(k, v) |
|
| 253 |
+ } |
|
| 254 |
+ } |
|
| 255 |
+ ht.rw.WriteHeader(200) |
|
| 256 |
+ ht.rw.(http.Flusher).Flush() |
|
| 257 |
+ }) |
|
| 258 |
+} |
|
| 259 |
+ |
|
| 260 |
+func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
|
|
| 261 |
+ // With this transport type there will be exactly 1 stream: this HTTP request. |
|
| 262 |
+ |
|
| 263 |
+ var ctx context.Context |
|
| 264 |
+ var cancel context.CancelFunc |
|
| 265 |
+ if ht.timeoutSet {
|
|
| 266 |
+ ctx, cancel = context.WithTimeout(context.Background(), ht.timeout) |
|
| 267 |
+ } else {
|
|
| 268 |
+ ctx, cancel = context.WithCancel(context.Background()) |
|
| 269 |
+ } |
|
| 270 |
+ |
|
| 271 |
+ // requestOver is closed when either the request's context is done |
|
| 272 |
+ // or the status has been written via WriteStatus. |
|
| 273 |
+ requestOver := make(chan struct{})
|
|
| 274 |
+ |
|
| 275 |
+ // clientGone receives a single value if peer is gone, either |
|
| 276 |
+ // because the underlying connection is dead or because the |
|
| 277 |
+ // peer sends an http2 RST_STREAM. |
|
| 278 |
+ clientGone := ht.rw.(http.CloseNotifier).CloseNotify() |
|
| 279 |
+ go func() {
|
|
| 280 |
+ select {
|
|
| 281 |
+ case <-requestOver: |
|
| 282 |
+ return |
|
| 283 |
+ case <-ht.closedCh: |
|
| 284 |
+ case <-clientGone: |
|
| 285 |
+ } |
|
| 286 |
+ cancel() |
|
| 287 |
+ }() |
|
| 288 |
+ |
|
| 289 |
+ req := ht.req |
|
| 290 |
+ |
|
| 291 |
+ s := &Stream{
|
|
| 292 |
+ id: 0, // irrelevant |
|
| 293 |
+ windowHandler: func(int) {}, // nothing
|
|
| 294 |
+ cancel: cancel, |
|
| 295 |
+ buf: newRecvBuffer(), |
|
| 296 |
+ st: ht, |
|
| 297 |
+ method: req.URL.Path, |
|
| 298 |
+ recvCompress: req.Header.Get("grpc-encoding"),
|
|
| 299 |
+ } |
|
| 300 |
+ pr := &peer.Peer{
|
|
| 301 |
+ Addr: ht.RemoteAddr(), |
|
| 302 |
+ } |
|
| 303 |
+ if req.TLS != nil {
|
|
| 304 |
+ pr.AuthInfo = credentials.TLSInfo{*req.TLS}
|
|
| 305 |
+ } |
|
| 306 |
+ ctx = metadata.NewContext(ctx, ht.headerMD) |
|
| 307 |
+ ctx = peer.NewContext(ctx, pr) |
|
| 308 |
+ s.ctx = newContextWithStream(ctx, s) |
|
| 309 |
+ s.dec = &recvBufferReader{ctx: s.ctx, recv: s.buf}
|
|
| 310 |
+ |
|
| 311 |
+ // readerDone is closed when the Body.Read-ing goroutine exits. |
|
| 312 |
+ readerDone := make(chan struct{})
|
|
| 313 |
+ go func() {
|
|
| 314 |
+ defer close(readerDone) |
|
| 315 |
+ |
|
| 316 |
+ // TODO: minimize garbage, optimize recvBuffer code/ownership |
|
| 317 |
+ const readSize = 8196 |
|
| 318 |
+ for buf := make([]byte, readSize); ; {
|
|
| 319 |
+ n, err := req.Body.Read(buf) |
|
| 320 |
+ if n > 0 {
|
|
| 321 |
+ s.buf.put(&recvMsg{data: buf[:n:n]})
|
|
| 322 |
+ buf = buf[n:] |
|
| 323 |
+ } |
|
| 324 |
+ if err != nil {
|
|
| 325 |
+ s.buf.put(&recvMsg{err: mapRecvMsgError(err)})
|
|
| 326 |
+ return |
|
| 327 |
+ } |
|
| 328 |
+ if len(buf) == 0 {
|
|
| 329 |
+ buf = make([]byte, readSize) |
|
| 330 |
+ } |
|
| 331 |
+ } |
|
| 332 |
+ }() |
|
| 333 |
+ |
|
| 334 |
+ // startStream is provided by the *grpc.Server's serveStreams. |
|
| 335 |
+ // It starts a goroutine serving s and exits immediately. |
|
| 336 |
+ // The goroutine that is started is the one that then calls |
|
| 337 |
+ // into ht, calling WriteHeader, Write, WriteStatus, Close, etc. |
|
| 338 |
+ startStream(s) |
|
| 339 |
+ |
|
| 340 |
+ ht.runStream() |
|
| 341 |
+ close(requestOver) |
|
| 342 |
+ |
|
| 343 |
+ // Wait for reading goroutine to finish. |
|
| 344 |
+ req.Body.Close() |
|
| 345 |
+ <-readerDone |
|
| 346 |
+} |
|
| 347 |
+ |
|
| 348 |
+func (ht *serverHandlerTransport) runStream() {
|
|
| 349 |
+ for {
|
|
| 350 |
+ select {
|
|
| 351 |
+ case fn, ok := <-ht.writes: |
|
| 352 |
+ if !ok {
|
|
| 353 |
+ return |
|
| 354 |
+ } |
|
| 355 |
+ fn() |
|
| 356 |
+ case <-ht.closedCh: |
|
| 357 |
+ return |
|
| 358 |
+ } |
|
| 359 |
+ } |
|
| 360 |
+} |
|
| 361 |
+ |
|
| 362 |
+// mapRecvMsgError returns the non-nil err into the appropriate |
|
| 363 |
+// error value as expected by callers of *grpc.parser.recvMsg. |
|
| 364 |
+// In particular, in can only be: |
|
| 365 |
+// * io.EOF |
|
| 366 |
+// * io.ErrUnexpectedEOF |
|
| 367 |
+// * of type transport.ConnectionError |
|
| 368 |
+// * of type transport.StreamError |
|
| 369 |
+func mapRecvMsgError(err error) error {
|
|
| 370 |
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
|
|
| 371 |
+ return err |
|
| 372 |
+ } |
|
| 373 |
+ if se, ok := err.(http2.StreamError); ok {
|
|
| 374 |
+ if code, ok := http2ErrConvTab[se.Code]; ok {
|
|
| 375 |
+ return StreamError{
|
|
| 376 |
+ Code: code, |
|
| 377 |
+ Desc: se.Error(), |
|
| 378 |
+ } |
|
| 379 |
+ } |
|
| 380 |
+ } |
|
| 381 |
+ return ConnectionError{Desc: err.Error()}
|
|
| 382 |
+} |
| ... | ... |
@@ -50,6 +50,7 @@ import ( |
| 50 | 50 |
"google.golang.org/grpc/credentials" |
| 51 | 51 |
"google.golang.org/grpc/grpclog" |
| 52 | 52 |
"google.golang.org/grpc/metadata" |
| 53 |
+ "google.golang.org/grpc/peer" |
|
| 53 | 54 |
) |
| 54 | 55 |
|
| 55 | 56 |
// http2Client implements the ClientTransport interface with HTTP2. |
| ... | ... |
@@ -139,29 +140,6 @@ func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err e |
| 139 | 139 |
conn.Close() |
| 140 | 140 |
} |
| 141 | 141 |
}() |
| 142 |
- // Send connection preface to server. |
|
| 143 |
- n, err := conn.Write(clientPreface) |
|
| 144 |
- if err != nil {
|
|
| 145 |
- return nil, ConnectionErrorf("transport: %v", err)
|
|
| 146 |
- } |
|
| 147 |
- if n != len(clientPreface) {
|
|
| 148 |
- return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
|
|
| 149 |
- } |
|
| 150 |
- framer := newFramer(conn) |
|
| 151 |
- if initialWindowSize != defaultWindowSize {
|
|
| 152 |
- err = framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
|
|
| 153 |
- } else {
|
|
| 154 |
- err = framer.writeSettings(true) |
|
| 155 |
- } |
|
| 156 |
- if err != nil {
|
|
| 157 |
- return nil, ConnectionErrorf("transport: %v", err)
|
|
| 158 |
- } |
|
| 159 |
- // Adjust the connection flow control window if needed. |
|
| 160 |
- if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
|
|
| 161 |
- if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
|
|
| 162 |
- return nil, ConnectionErrorf("transport: %v", err)
|
|
| 163 |
- } |
|
| 164 |
- } |
|
| 165 | 142 |
ua := primaryUA |
| 166 | 143 |
if opts.UserAgent != "" {
|
| 167 | 144 |
ua = opts.UserAgent + " " + ua |
| ... | ... |
@@ -177,7 +155,7 @@ func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err e |
| 177 | 177 |
writableChan: make(chan int, 1), |
| 178 | 178 |
shutdownChan: make(chan struct{}),
|
| 179 | 179 |
errorChan: make(chan struct{}),
|
| 180 |
- framer: framer, |
|
| 180 |
+ framer: newFramer(conn), |
|
| 181 | 181 |
hBuf: &buf, |
| 182 | 182 |
hEnc: hpack.NewEncoder(&buf), |
| 183 | 183 |
controlBuf: newRecvBuffer(), |
| ... | ... |
@@ -190,27 +168,49 @@ func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err e |
| 190 | 190 |
maxStreams: math.MaxInt32, |
| 191 | 191 |
streamSendQuota: defaultWindowSize, |
| 192 | 192 |
} |
| 193 |
+ // Start the reader goroutine for incoming message. Each transport has |
|
| 194 |
+ // a dedicated goroutine which reads HTTP2 frame from network. Then it |
|
| 195 |
+ // dispatches the frame to the corresponding stream entity. |
|
| 196 |
+ go t.reader() |
|
| 197 |
+ // Send connection preface to server. |
|
| 198 |
+ n, err := t.conn.Write(clientPreface) |
|
| 199 |
+ if err != nil {
|
|
| 200 |
+ t.Close() |
|
| 201 |
+ return nil, ConnectionErrorf("transport: %v", err)
|
|
| 202 |
+ } |
|
| 203 |
+ if n != len(clientPreface) {
|
|
| 204 |
+ t.Close() |
|
| 205 |
+ return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
|
|
| 206 |
+ } |
|
| 207 |
+ if initialWindowSize != defaultWindowSize {
|
|
| 208 |
+ err = t.framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
|
|
| 209 |
+ } else {
|
|
| 210 |
+ err = t.framer.writeSettings(true) |
|
| 211 |
+ } |
|
| 212 |
+ if err != nil {
|
|
| 213 |
+ t.Close() |
|
| 214 |
+ return nil, ConnectionErrorf("transport: %v", err)
|
|
| 215 |
+ } |
|
| 216 |
+ // Adjust the connection flow control window if needed. |
|
| 217 |
+ if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
|
|
| 218 |
+ if err := t.framer.writeWindowUpdate(true, 0, delta); err != nil {
|
|
| 219 |
+ t.Close() |
|
| 220 |
+ return nil, ConnectionErrorf("transport: %v", err)
|
|
| 221 |
+ } |
|
| 222 |
+ } |
|
| 193 | 223 |
go t.controller() |
| 194 | 224 |
t.writableChan <- 0 |
| 195 |
- // Start the reader goroutine for incoming message. The threading model |
|
| 196 |
- // on receiving is that each transport has a dedicated goroutine which |
|
| 197 |
- // reads HTTP2 frame from network. Then it dispatches the frame to the |
|
| 198 |
- // corresponding stream entity. |
|
| 199 |
- go t.reader() |
|
| 200 | 225 |
return t, nil |
| 201 | 226 |
} |
| 202 | 227 |
|
| 203 | 228 |
func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
| 204 |
- fc := &inFlow{
|
|
| 205 |
- limit: initialWindowSize, |
|
| 206 |
- conn: t.fc, |
|
| 207 |
- } |
|
| 208 | 229 |
// TODO(zhaoq): Handle uint32 overflow of Stream.id. |
| 209 | 230 |
s := &Stream{
|
| 210 | 231 |
id: t.nextID, |
| 211 | 232 |
method: callHdr.Method, |
| 233 |
+ sendCompress: callHdr.SendCompress, |
|
| 212 | 234 |
buf: newRecvBuffer(), |
| 213 |
- fc: fc, |
|
| 235 |
+ fc: &inFlow{limit: initialWindowSize},
|
|
| 214 | 236 |
sendQuotaPool: newQuotaPool(int(t.streamSendQuota)), |
| 215 | 237 |
headerChan: make(chan struct{}),
|
| 216 | 238 |
} |
| ... | ... |
@@ -234,14 +234,20 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea |
| 234 | 234 |
var timeout time.Duration |
| 235 | 235 |
if dl, ok := ctx.Deadline(); ok {
|
| 236 | 236 |
timeout = dl.Sub(time.Now()) |
| 237 |
- if timeout <= 0 {
|
|
| 238 |
- return nil, ContextErr(context.DeadlineExceeded) |
|
| 239 |
- } |
|
| 237 |
+ } |
|
| 238 |
+ select {
|
|
| 239 |
+ case <-ctx.Done(): |
|
| 240 |
+ return nil, ContextErr(ctx.Err()) |
|
| 241 |
+ default: |
|
| 242 |
+ } |
|
| 243 |
+ pr := &peer.Peer{
|
|
| 244 |
+ Addr: t.conn.RemoteAddr(), |
|
| 240 | 245 |
} |
| 241 | 246 |
// Attach Auth info if there is any. |
| 242 | 247 |
if t.authInfo != nil {
|
| 243 |
- ctx = credentials.NewContext(ctx, t.authInfo) |
|
| 248 |
+ pr.AuthInfo = t.authInfo |
|
| 244 | 249 |
} |
| 250 |
+ ctx = peer.NewContext(ctx, pr) |
|
| 245 | 251 |
authData := make(map[string]string) |
| 246 | 252 |
for _, c := range t.authCreds {
|
| 247 | 253 |
// Construct URI required to get auth request metadata. |
| ... | ... |
@@ -317,10 +323,15 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea |
| 317 | 317 |
t.hEnc.WriteField(hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
|
| 318 | 318 |
t.hEnc.WriteField(hpack.HeaderField{Name: "te", Value: "trailers"})
|
| 319 | 319 |
|
| 320 |
+ if callHdr.SendCompress != "" {
|
|
| 321 |
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress})
|
|
| 322 |
+ } |
|
| 320 | 323 |
if timeout > 0 {
|
| 321 | 324 |
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-timeout", Value: timeoutEncode(timeout)})
|
| 322 | 325 |
} |
| 323 | 326 |
for k, v := range authData {
|
| 327 |
+ // Capital header names are illegal in HTTP/2. |
|
| 328 |
+ k = strings.ToLower(k) |
|
| 324 | 329 |
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
|
| 325 | 330 |
} |
| 326 | 331 |
var ( |
| ... | ... |
@@ -344,6 +355,10 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea |
| 344 | 344 |
} else {
|
| 345 | 345 |
endHeaders = true |
| 346 | 346 |
} |
| 347 |
+ var flush bool |
|
| 348 |
+ if endHeaders && (hasMD || callHdr.Flush) {
|
|
| 349 |
+ flush = true |
|
| 350 |
+ } |
|
| 347 | 351 |
if first {
|
| 348 | 352 |
// Sends a HeadersFrame to server to start a new stream. |
| 349 | 353 |
p := http2.HeadersFrameParam{
|
| ... | ... |
@@ -355,11 +370,11 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea |
| 355 | 355 |
// Do a force flush for the buffered frames iff it is the last headers frame |
| 356 | 356 |
// and there is header metadata to be sent. Otherwise, there is flushing until |
| 357 | 357 |
// the corresponding data frame is written. |
| 358 |
- err = t.framer.writeHeaders(hasMD && endHeaders, p) |
|
| 358 |
+ err = t.framer.writeHeaders(flush, p) |
|
| 359 | 359 |
first = false |
| 360 | 360 |
} else {
|
| 361 | 361 |
// Sends Continuation frames for the leftover headers. |
| 362 |
- err = t.framer.writeContinuation(hasMD && endHeaders, s.id, endHeaders, t.hBuf.Next(size)) |
|
| 362 |
+ err = t.framer.writeContinuation(flush, s.id, endHeaders, t.hBuf.Next(size)) |
|
| 363 | 363 |
} |
| 364 | 364 |
if err != nil {
|
| 365 | 365 |
t.notifyError(err) |
| ... | ... |
@@ -389,8 +404,10 @@ func (t *http2Client) CloseStream(s *Stream, err error) {
|
| 389 | 389 |
// other goroutines. |
| 390 | 390 |
s.cancel() |
| 391 | 391 |
s.mu.Lock() |
| 392 |
- if q := s.fc.restoreConn(); q > 0 {
|
|
| 393 |
- t.controlBuf.put(&windowUpdate{0, q})
|
|
| 392 |
+ if q := s.fc.resetPendingData(); q > 0 {
|
|
| 393 |
+ if n := t.fc.onRead(q); n > 0 {
|
|
| 394 |
+ t.controlBuf.put(&windowUpdate{0, n})
|
|
| 395 |
+ } |
|
| 394 | 396 |
} |
| 395 | 397 |
if s.state == streamDone {
|
| 396 | 398 |
s.mu.Unlock() |
| ... | ... |
@@ -412,6 +429,9 @@ func (t *http2Client) CloseStream(s *Stream, err error) {
|
| 412 | 412 |
// accessed any more. |
| 413 | 413 |
func (t *http2Client) Close() (err error) {
|
| 414 | 414 |
t.mu.Lock() |
| 415 |
+ if t.state == reachable {
|
|
| 416 |
+ close(t.errorChan) |
|
| 417 |
+ } |
|
| 415 | 418 |
if t.state == closing {
|
| 416 | 419 |
t.mu.Unlock() |
| 417 | 420 |
return errors.New("transport: Close() was already called")
|
| ... | ... |
@@ -490,6 +510,10 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
|
| 490 | 490 |
t.framer.adjustNumWriters(1) |
| 491 | 491 |
// Got some quota. Try to acquire writing privilege on the transport. |
| 492 | 492 |
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
|
| 493 |
+ if _, ok := err.(StreamError); ok {
|
|
| 494 |
+ // Return the connection quota back. |
|
| 495 |
+ t.sendQuotaPool.add(len(p)) |
|
| 496 |
+ } |
|
| 493 | 497 |
if t.framer.adjustNumWriters(-1) == 0 {
|
| 494 | 498 |
// This writer is the last one in this batch and has the |
| 495 | 499 |
// responsibility to flush the buffered frames. It queues |
| ... | ... |
@@ -499,6 +523,16 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
|
| 499 | 499 |
} |
| 500 | 500 |
return err |
| 501 | 501 |
} |
| 502 |
+ select {
|
|
| 503 |
+ case <-s.ctx.Done(): |
|
| 504 |
+ t.sendQuotaPool.add(len(p)) |
|
| 505 |
+ if t.framer.adjustNumWriters(-1) == 0 {
|
|
| 506 |
+ t.controlBuf.put(&flushIO{})
|
|
| 507 |
+ } |
|
| 508 |
+ t.writableChan <- 0 |
|
| 509 |
+ return ContextErr(s.ctx.Err()) |
|
| 510 |
+ default: |
|
| 511 |
+ } |
|
| 502 | 512 |
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 {
|
| 503 | 513 |
// Do a force flush iff this is last frame for the entire gRPC message |
| 504 | 514 |
// and the caller is the only writer at this moment. |
| ... | ... |
@@ -537,47 +571,52 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
|
| 537 | 537 |
func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
|
| 538 | 538 |
t.mu.Lock() |
| 539 | 539 |
defer t.mu.Unlock() |
| 540 |
- if t.activeStreams == nil {
|
|
| 541 |
- // The transport is closing. |
|
| 542 |
- return nil, false |
|
| 543 |
- } |
|
| 544 |
- if s, ok := t.activeStreams[f.Header().StreamID]; ok {
|
|
| 545 |
- return s, true |
|
| 546 |
- } |
|
| 547 |
- return nil, false |
|
| 540 |
+ s, ok := t.activeStreams[f.Header().StreamID] |
|
| 541 |
+ return s, ok |
|
| 548 | 542 |
} |
| 549 | 543 |
|
| 550 | 544 |
// updateWindow adjusts the inbound quota for the stream and the transport. |
| 551 | 545 |
// Window updates will deliver to the controller for sending when |
| 552 | 546 |
// the cumulative quota exceeds the corresponding threshold. |
| 553 | 547 |
func (t *http2Client) updateWindow(s *Stream, n uint32) {
|
| 554 |
- swu, cwu := s.fc.onRead(n) |
|
| 555 |
- if swu > 0 {
|
|
| 556 |
- t.controlBuf.put(&windowUpdate{s.id, swu})
|
|
| 548 |
+ s.mu.Lock() |
|
| 549 |
+ defer s.mu.Unlock() |
|
| 550 |
+ if s.state == streamDone {
|
|
| 551 |
+ return |
|
| 552 |
+ } |
|
| 553 |
+ if w := t.fc.onRead(n); w > 0 {
|
|
| 554 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 557 | 555 |
} |
| 558 |
- if cwu > 0 {
|
|
| 559 |
- t.controlBuf.put(&windowUpdate{0, cwu})
|
|
| 556 |
+ if w := s.fc.onRead(n); w > 0 {
|
|
| 557 |
+ t.controlBuf.put(&windowUpdate{s.id, w})
|
|
| 560 | 558 |
} |
| 561 | 559 |
} |
| 562 | 560 |
|
| 563 | 561 |
func (t *http2Client) handleData(f *http2.DataFrame) {
|
| 562 |
+ size := len(f.Data()) |
|
| 563 |
+ if err := t.fc.onData(uint32(size)); err != nil {
|
|
| 564 |
+ t.notifyError(ConnectionErrorf("%v", err))
|
|
| 565 |
+ return |
|
| 566 |
+ } |
|
| 564 | 567 |
// Select the right stream to dispatch. |
| 565 | 568 |
s, ok := t.getStream(f) |
| 566 | 569 |
if !ok {
|
| 570 |
+ if w := t.fc.onRead(uint32(size)); w > 0 {
|
|
| 571 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 572 |
+ } |
|
| 567 | 573 |
return |
| 568 | 574 |
} |
| 569 |
- size := len(f.Data()) |
|
| 570 | 575 |
if size > 0 {
|
| 571 |
- if err := s.fc.onData(uint32(size)); err != nil {
|
|
| 572 |
- if _, ok := err.(ConnectionError); ok {
|
|
| 573 |
- t.notifyError(err) |
|
| 574 |
- return |
|
| 575 |
- } |
|
| 576 |
- s.mu.Lock() |
|
| 577 |
- if s.state == streamDone {
|
|
| 578 |
- s.mu.Unlock() |
|
| 579 |
- return |
|
| 576 |
+ s.mu.Lock() |
|
| 577 |
+ if s.state == streamDone {
|
|
| 578 |
+ s.mu.Unlock() |
|
| 579 |
+ // The stream has been closed. Release the corresponding quota. |
|
| 580 |
+ if w := t.fc.onRead(uint32(size)); w > 0 {
|
|
| 581 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 580 | 582 |
} |
| 583 |
+ return |
|
| 584 |
+ } |
|
| 585 |
+ if err := s.fc.onData(uint32(size)); err != nil {
|
|
| 581 | 586 |
s.state = streamDone |
| 582 | 587 |
s.statusCode = codes.Internal |
| 583 | 588 |
s.statusDesc = err.Error() |
| ... | ... |
@@ -586,6 +625,7 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
| 586 | 586 |
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
|
| 587 | 587 |
return |
| 588 | 588 |
} |
| 589 |
+ s.mu.Unlock() |
|
| 589 | 590 |
// TODO(bradfitz, zhaoq): A copy is required here because there is no |
| 590 | 591 |
// guarantee f.Data() is consumed before the arrival of next frame. |
| 591 | 592 |
// Can this copy be eliminated? |
| ... | ... |
@@ -624,9 +664,10 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
|
| 624 | 624 |
close(s.headerChan) |
| 625 | 625 |
s.headerDone = true |
| 626 | 626 |
} |
| 627 |
- s.statusCode, ok = http2RSTErrConvTab[http2.ErrCode(f.ErrCode)] |
|
| 627 |
+ s.statusCode, ok = http2ErrConvTab[http2.ErrCode(f.ErrCode)] |
|
| 628 | 628 |
if !ok {
|
| 629 | 629 |
grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode)
|
| 630 |
+ s.statusCode = codes.Unknown |
|
| 630 | 631 |
} |
| 631 | 632 |
s.mu.Unlock() |
| 632 | 633 |
s.write(recvMsg{err: io.EOF})
|
| ... | ... |
@@ -667,52 +708,59 @@ func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
| 667 | 667 |
} |
| 668 | 668 |
} |
| 669 | 669 |
|
| 670 |
-// operateHeader takes action on the decoded headers. It returns the current |
|
| 671 |
-// stream if there are remaining headers on the wire (in the following |
|
| 672 |
-// Continuation frame). |
|
| 673 |
-func (t *http2Client) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool) (pendingStream *Stream) {
|
|
| 674 |
- defer func() {
|
|
| 675 |
- if pendingStream == nil {
|
|
| 676 |
- hDec.state = decodeState{}
|
|
| 677 |
- } |
|
| 678 |
- }() |
|
| 679 |
- endHeaders, err := hDec.decodeClientHTTP2Headers(frame) |
|
| 680 |
- if s == nil {
|
|
| 681 |
- // s has been closed. |
|
| 682 |
- return nil |
|
| 670 |
+// operateHeaders takes action on the decoded headers. |
|
| 671 |
+func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
|
| 672 |
+ s, ok := t.getStream(frame) |
|
| 673 |
+ if !ok {
|
|
| 674 |
+ return |
|
| 683 | 675 |
} |
| 684 |
- if err != nil {
|
|
| 685 |
- s.write(recvMsg{err: err})
|
|
| 686 |
- // Something wrong. Stops reading even when there is remaining. |
|
| 687 |
- return nil |
|
| 676 |
+ var state decodeState |
|
| 677 |
+ for _, hf := range frame.Fields {
|
|
| 678 |
+ state.processHeaderField(hf) |
|
| 688 | 679 |
} |
| 689 |
- if !endHeaders {
|
|
| 690 |
- return s |
|
| 680 |
+ if state.err != nil {
|
|
| 681 |
+ s.write(recvMsg{err: state.err})
|
|
| 682 |
+ // Something wrong. Stops reading even when there is remaining. |
|
| 683 |
+ return |
|
| 691 | 684 |
} |
| 692 | 685 |
|
| 686 |
+ endStream := frame.StreamEnded() |
|
| 687 |
+ |
|
| 693 | 688 |
s.mu.Lock() |
| 689 |
+ if !endStream {
|
|
| 690 |
+ s.recvCompress = state.encoding |
|
| 691 |
+ } |
|
| 694 | 692 |
if !s.headerDone {
|
| 695 |
- if !endStream && len(hDec.state.mdata) > 0 {
|
|
| 696 |
- s.header = hDec.state.mdata |
|
| 693 |
+ if !endStream && len(state.mdata) > 0 {
|
|
| 694 |
+ s.header = state.mdata |
|
| 697 | 695 |
} |
| 698 | 696 |
close(s.headerChan) |
| 699 | 697 |
s.headerDone = true |
| 700 | 698 |
} |
| 701 | 699 |
if !endStream || s.state == streamDone {
|
| 702 | 700 |
s.mu.Unlock() |
| 703 |
- return nil |
|
| 701 |
+ return |
|
| 704 | 702 |
} |
| 705 | 703 |
|
| 706 |
- if len(hDec.state.mdata) > 0 {
|
|
| 707 |
- s.trailer = hDec.state.mdata |
|
| 704 |
+ if len(state.mdata) > 0 {
|
|
| 705 |
+ s.trailer = state.mdata |
|
| 708 | 706 |
} |
| 709 | 707 |
s.state = streamDone |
| 710 |
- s.statusCode = hDec.state.statusCode |
|
| 711 |
- s.statusDesc = hDec.state.statusDesc |
|
| 708 |
+ s.statusCode = state.statusCode |
|
| 709 |
+ s.statusDesc = state.statusDesc |
|
| 712 | 710 |
s.mu.Unlock() |
| 713 | 711 |
|
| 714 | 712 |
s.write(recvMsg{err: io.EOF})
|
| 715 |
- return nil |
|
| 713 |
+} |
|
| 714 |
+ |
|
| 715 |
+func handleMalformedHTTP2(s *Stream, err error) {
|
|
| 716 |
+ s.mu.Lock() |
|
| 717 |
+ if !s.headerDone {
|
|
| 718 |
+ close(s.headerChan) |
|
| 719 |
+ s.headerDone = true |
|
| 720 |
+ } |
|
| 721 |
+ s.mu.Unlock() |
|
| 722 |
+ s.write(recvMsg{err: err})
|
|
| 716 | 723 |
} |
| 717 | 724 |
|
| 718 | 725 |
// reader runs as a separate goroutine in charge of reading data from network |
| ... | ... |
@@ -735,25 +783,31 @@ func (t *http2Client) reader() {
|
| 735 | 735 |
} |
| 736 | 736 |
t.handleSettings(sf) |
| 737 | 737 |
|
| 738 |
- hDec := newHPACKDecoder() |
|
| 739 |
- var curStream *Stream |
|
| 740 | 738 |
// loop to keep reading incoming messages on this transport. |
| 741 | 739 |
for {
|
| 742 | 740 |
frame, err := t.framer.readFrame() |
| 743 | 741 |
if err != nil {
|
| 744 |
- t.notifyError(err) |
|
| 745 |
- return |
|
| 742 |
+ // Abort an active stream if the http2.Framer returns a |
|
| 743 |
+ // http2.StreamError. This can happen only if the server's response |
|
| 744 |
+ // is malformed http2. |
|
| 745 |
+ if se, ok := err.(http2.StreamError); ok {
|
|
| 746 |
+ t.mu.Lock() |
|
| 747 |
+ s := t.activeStreams[se.StreamID] |
|
| 748 |
+ t.mu.Unlock() |
|
| 749 |
+ if s != nil {
|
|
| 750 |
+ // use error detail to provide better err message |
|
| 751 |
+ handleMalformedHTTP2(s, StreamErrorf(http2ErrConvTab[se.Code], "%v", t.framer.errorDetail())) |
|
| 752 |
+ } |
|
| 753 |
+ continue |
|
| 754 |
+ } else {
|
|
| 755 |
+ // Transport error. |
|
| 756 |
+ t.notifyError(err) |
|
| 757 |
+ return |
|
| 758 |
+ } |
|
| 746 | 759 |
} |
| 747 | 760 |
switch frame := frame.(type) {
|
| 748 |
- case *http2.HeadersFrame: |
|
| 749 |
- // operateHeaders has to be invoked regardless the value of curStream |
|
| 750 |
- // because the HPACK decoder needs to be updated using the received |
|
| 751 |
- // headers. |
|
| 752 |
- curStream, _ = t.getStream(frame) |
|
| 753 |
- endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream) |
|
| 754 |
- curStream = t.operateHeaders(hDec, curStream, frame, endStream) |
|
| 755 |
- case *http2.ContinuationFrame: |
|
| 756 |
- curStream = t.operateHeaders(hDec, curStream, frame, frame.HeadersEnded()) |
|
| 761 |
+ case *http2.MetaHeadersFrame: |
|
| 762 |
+ t.operateHeaders(frame) |
|
| 757 | 763 |
case *http2.DataFrame: |
| 758 | 764 |
t.handleData(frame) |
| 759 | 765 |
case *http2.RSTStreamFrame: |
| ... | ... |
@@ -49,6 +49,7 @@ import ( |
| 49 | 49 |
"google.golang.org/grpc/credentials" |
| 50 | 50 |
"google.golang.org/grpc/grpclog" |
| 51 | 51 |
"google.golang.org/grpc/metadata" |
| 52 |
+ "google.golang.org/grpc/peer" |
|
| 52 | 53 |
) |
| 53 | 54 |
|
| 54 | 55 |
// ErrIllegalHeaderWrite indicates that setting header is illegal because of |
| ... | ... |
@@ -61,8 +62,8 @@ type http2Server struct {
|
| 61 | 61 |
maxStreamID uint32 // max stream ID ever seen |
| 62 | 62 |
authInfo credentials.AuthInfo // auth info about the connection |
| 63 | 63 |
// writableChan synchronizes write access to the transport. |
| 64 |
- // A writer acquires the write lock by sending a value on writableChan |
|
| 65 |
- // and releases it by receiving from writableChan. |
|
| 64 |
+ // A writer acquires the write lock by receiving a value on writableChan |
|
| 65 |
+ // and releases it by sending on writableChan. |
|
| 66 | 66 |
writableChan chan int |
| 67 | 67 |
// shutdownChan is closed when Close is called. |
| 68 | 68 |
// Blocking operations should select on shutdownChan to avoid |
| ... | ... |
@@ -135,66 +136,69 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI |
| 135 | 135 |
return t, nil |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 |
-// operateHeader takes action on the decoded headers. It returns the current |
|
| 139 |
-// stream if there are remaining headers on the wire (in the following |
|
| 140 |
-// Continuation frame). |
|
| 141 |
-func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool, handle func(*Stream)) (pendingStream *Stream) {
|
|
| 142 |
- defer func() {
|
|
| 143 |
- if pendingStream == nil {
|
|
| 144 |
- hDec.state = decodeState{}
|
|
| 145 |
- } |
|
| 146 |
- }() |
|
| 147 |
- endHeaders, err := hDec.decodeServerHTTP2Headers(frame) |
|
| 148 |
- if s == nil {
|
|
| 149 |
- // s has been closed. |
|
| 150 |
- return nil |
|
| 138 |
+// operateHeader takes action on the decoded headers. |
|
| 139 |
+func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) {
|
|
| 140 |
+ buf := newRecvBuffer() |
|
| 141 |
+ s := &Stream{
|
|
| 142 |
+ id: frame.Header().StreamID, |
|
| 143 |
+ st: t, |
|
| 144 |
+ buf: buf, |
|
| 145 |
+ fc: &inFlow{limit: initialWindowSize},
|
|
| 151 | 146 |
} |
| 152 |
- if err != nil {
|
|
| 153 |
- grpclog.Printf("transport: http2Server.operateHeader found %v", err)
|
|
| 147 |
+ |
|
| 148 |
+ var state decodeState |
|
| 149 |
+ for _, hf := range frame.Fields {
|
|
| 150 |
+ state.processHeaderField(hf) |
|
| 151 |
+ } |
|
| 152 |
+ if err := state.err; err != nil {
|
|
| 154 | 153 |
if se, ok := err.(StreamError); ok {
|
| 155 | 154 |
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
|
| 156 | 155 |
} |
| 157 |
- return nil |
|
| 156 |
+ return |
|
| 158 | 157 |
} |
| 159 |
- if endStream {
|
|
| 158 |
+ |
|
| 159 |
+ if frame.StreamEnded() {
|
|
| 160 | 160 |
// s is just created by the caller. No lock needed. |
| 161 | 161 |
s.state = streamReadDone |
| 162 | 162 |
} |
| 163 |
- if !endHeaders {
|
|
| 164 |
- return s |
|
| 165 |
- } |
|
| 166 |
- if hDec.state.timeoutSet {
|
|
| 167 |
- s.ctx, s.cancel = context.WithTimeout(context.TODO(), hDec.state.timeout) |
|
| 163 |
+ s.recvCompress = state.encoding |
|
| 164 |
+ if state.timeoutSet {
|
|
| 165 |
+ s.ctx, s.cancel = context.WithTimeout(context.TODO(), state.timeout) |
|
| 168 | 166 |
} else {
|
| 169 | 167 |
s.ctx, s.cancel = context.WithCancel(context.TODO()) |
| 170 | 168 |
} |
| 169 |
+ pr := &peer.Peer{
|
|
| 170 |
+ Addr: t.conn.RemoteAddr(), |
|
| 171 |
+ } |
|
| 171 | 172 |
// Attach Auth info if there is any. |
| 172 | 173 |
if t.authInfo != nil {
|
| 173 |
- s.ctx = credentials.NewContext(s.ctx, t.authInfo) |
|
| 174 |
+ pr.AuthInfo = t.authInfo |
|
| 174 | 175 |
} |
| 176 |
+ s.ctx = peer.NewContext(s.ctx, pr) |
|
| 175 | 177 |
// Cache the current stream to the context so that the server application |
| 176 | 178 |
// can find out. Required when the server wants to send some metadata |
| 177 | 179 |
// back to the client (unary call only). |
| 178 | 180 |
s.ctx = newContextWithStream(s.ctx, s) |
| 179 | 181 |
// Attach the received metadata to the context. |
| 180 |
- if len(hDec.state.mdata) > 0 {
|
|
| 181 |
- s.ctx = metadata.NewContext(s.ctx, hDec.state.mdata) |
|
| 182 |
+ if len(state.mdata) > 0 {
|
|
| 183 |
+ s.ctx = metadata.NewContext(s.ctx, state.mdata) |
|
| 182 | 184 |
} |
| 183 | 185 |
|
| 184 | 186 |
s.dec = &recvBufferReader{
|
| 185 | 187 |
ctx: s.ctx, |
| 186 | 188 |
recv: s.buf, |
| 187 | 189 |
} |
| 188 |
- s.method = hDec.state.method |
|
| 190 |
+ s.recvCompress = state.encoding |
|
| 191 |
+ s.method = state.method |
|
| 189 | 192 |
t.mu.Lock() |
| 190 | 193 |
if t.state != reachable {
|
| 191 | 194 |
t.mu.Unlock() |
| 192 |
- return nil |
|
| 195 |
+ return |
|
| 193 | 196 |
} |
| 194 | 197 |
if uint32(len(t.activeStreams)) >= t.maxStreams {
|
| 195 | 198 |
t.mu.Unlock() |
| 196 | 199 |
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
|
| 197 |
- return nil |
|
| 200 |
+ return |
|
| 198 | 201 |
} |
| 199 | 202 |
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota)) |
| 200 | 203 |
t.activeStreams[s.id] = s |
| ... | ... |
@@ -203,7 +207,6 @@ func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame header |
| 203 | 203 |
t.updateWindow(s, uint32(n)) |
| 204 | 204 |
} |
| 205 | 205 |
handle(s) |
| 206 |
- return nil |
|
| 207 | 206 |
} |
| 208 | 207 |
|
| 209 | 208 |
// HandleStreams receives incoming streams using the given handler. This is |
| ... | ... |
@@ -236,16 +239,24 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
| 236 | 236 |
} |
| 237 | 237 |
t.handleSettings(sf) |
| 238 | 238 |
|
| 239 |
- hDec := newHPACKDecoder() |
|
| 240 |
- var curStream *Stream |
|
| 241 | 239 |
for {
|
| 242 | 240 |
frame, err := t.framer.readFrame() |
| 243 | 241 |
if err != nil {
|
| 242 |
+ if se, ok := err.(http2.StreamError); ok {
|
|
| 243 |
+ t.mu.Lock() |
|
| 244 |
+ s := t.activeStreams[se.StreamID] |
|
| 245 |
+ t.mu.Unlock() |
|
| 246 |
+ if s != nil {
|
|
| 247 |
+ t.closeStream(s) |
|
| 248 |
+ } |
|
| 249 |
+ t.controlBuf.put(&resetStream{se.StreamID, se.Code})
|
|
| 250 |
+ continue |
|
| 251 |
+ } |
|
| 244 | 252 |
t.Close() |
| 245 | 253 |
return |
| 246 | 254 |
} |
| 247 | 255 |
switch frame := frame.(type) {
|
| 248 |
- case *http2.HeadersFrame: |
|
| 256 |
+ case *http2.MetaHeadersFrame: |
|
| 249 | 257 |
id := frame.Header().StreamID |
| 250 | 258 |
if id%2 != 1 || id <= t.maxStreamID {
|
| 251 | 259 |
// illegal gRPC stream id. |
| ... | ... |
@@ -254,21 +265,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
| 254 | 254 |
break |
| 255 | 255 |
} |
| 256 | 256 |
t.maxStreamID = id |
| 257 |
- buf := newRecvBuffer() |
|
| 258 |
- fc := &inFlow{
|
|
| 259 |
- limit: initialWindowSize, |
|
| 260 |
- conn: t.fc, |
|
| 261 |
- } |
|
| 262 |
- curStream = &Stream{
|
|
| 263 |
- id: frame.Header().StreamID, |
|
| 264 |
- st: t, |
|
| 265 |
- buf: buf, |
|
| 266 |
- fc: fc, |
|
| 267 |
- } |
|
| 268 |
- endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream) |
|
| 269 |
- curStream = t.operateHeaders(hDec, curStream, frame, endStream, handle) |
|
| 270 |
- case *http2.ContinuationFrame: |
|
| 271 |
- curStream = t.operateHeaders(hDec, curStream, frame, frame.HeadersEnded(), handle) |
|
| 257 |
+ t.operateHeaders(frame, handle) |
|
| 272 | 258 |
case *http2.DataFrame: |
| 273 | 259 |
t.handleData(frame) |
| 274 | 260 |
case *http2.RSTStreamFrame: |
| ... | ... |
@@ -306,33 +303,51 @@ func (t *http2Server) getStream(f http2.Frame) (*Stream, bool) {
|
| 306 | 306 |
// Window updates will deliver to the controller for sending when |
| 307 | 307 |
// the cumulative quota exceeds the corresponding threshold. |
| 308 | 308 |
func (t *http2Server) updateWindow(s *Stream, n uint32) {
|
| 309 |
- swu, cwu := s.fc.onRead(n) |
|
| 310 |
- if swu > 0 {
|
|
| 311 |
- t.controlBuf.put(&windowUpdate{s.id, swu})
|
|
| 309 |
+ s.mu.Lock() |
|
| 310 |
+ defer s.mu.Unlock() |
|
| 311 |
+ if s.state == streamDone {
|
|
| 312 |
+ return |
|
| 312 | 313 |
} |
| 313 |
- if cwu > 0 {
|
|
| 314 |
- t.controlBuf.put(&windowUpdate{0, cwu})
|
|
| 314 |
+ if w := t.fc.onRead(n); w > 0 {
|
|
| 315 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 316 |
+ } |
|
| 317 |
+ if w := s.fc.onRead(n); w > 0 {
|
|
| 318 |
+ t.controlBuf.put(&windowUpdate{s.id, w})
|
|
| 315 | 319 |
} |
| 316 | 320 |
} |
| 317 | 321 |
|
| 318 | 322 |
func (t *http2Server) handleData(f *http2.DataFrame) {
|
| 323 |
+ size := len(f.Data()) |
|
| 324 |
+ if err := t.fc.onData(uint32(size)); err != nil {
|
|
| 325 |
+ grpclog.Printf("transport: http2Server %v", err)
|
|
| 326 |
+ t.Close() |
|
| 327 |
+ return |
|
| 328 |
+ } |
|
| 319 | 329 |
// Select the right stream to dispatch. |
| 320 | 330 |
s, ok := t.getStream(f) |
| 321 | 331 |
if !ok {
|
| 332 |
+ if w := t.fc.onRead(uint32(size)); w > 0 {
|
|
| 333 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 334 |
+ } |
|
| 322 | 335 |
return |
| 323 | 336 |
} |
| 324 |
- size := len(f.Data()) |
|
| 325 | 337 |
if size > 0 {
|
| 326 |
- if err := s.fc.onData(uint32(size)); err != nil {
|
|
| 327 |
- if _, ok := err.(ConnectionError); ok {
|
|
| 328 |
- grpclog.Printf("transport: http2Server %v", err)
|
|
| 329 |
- t.Close() |
|
| 330 |
- return |
|
| 338 |
+ s.mu.Lock() |
|
| 339 |
+ if s.state == streamDone {
|
|
| 340 |
+ s.mu.Unlock() |
|
| 341 |
+ // The stream has been closed. Release the corresponding quota. |
|
| 342 |
+ if w := t.fc.onRead(uint32(size)); w > 0 {
|
|
| 343 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 331 | 344 |
} |
| 345 |
+ return |
|
| 346 |
+ } |
|
| 347 |
+ if err := s.fc.onData(uint32(size)); err != nil {
|
|
| 348 |
+ s.mu.Unlock() |
|
| 332 | 349 |
t.closeStream(s) |
| 333 | 350 |
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
|
| 334 | 351 |
return |
| 335 | 352 |
} |
| 353 |
+ s.mu.Unlock() |
|
| 336 | 354 |
// TODO(bradfitz, zhaoq): A copy is required here because there is no |
| 337 | 355 |
// guarantee f.Data() is consumed before the arrival of next frame. |
| 338 | 356 |
// Can this copy be eliminated? |
| ... | ... |
@@ -441,6 +456,9 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
| 441 | 441 |
t.hBuf.Reset() |
| 442 | 442 |
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
|
| 443 | 443 |
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
|
| 444 |
+ if s.sendCompress != "" {
|
|
| 445 |
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
|
| 446 |
+ } |
|
| 444 | 447 |
for k, v := range md {
|
| 445 | 448 |
for _, entry := range v {
|
| 446 | 449 |
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
| ... | ... |
@@ -503,6 +521,10 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
|
| 503 | 503 |
// TODO(zhaoq): Support multi-writers for a single stream. |
| 504 | 504 |
var writeHeaderFrame bool |
| 505 | 505 |
s.mu.Lock() |
| 506 |
+ if s.state == streamDone {
|
|
| 507 |
+ s.mu.Unlock() |
|
| 508 |
+ return StreamErrorf(codes.Unknown, "the stream has been done") |
|
| 509 |
+ } |
|
| 506 | 510 |
if !s.headerOk {
|
| 507 | 511 |
writeHeaderFrame = true |
| 508 | 512 |
s.headerOk = true |
| ... | ... |
@@ -515,6 +537,9 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
|
| 515 | 515 |
t.hBuf.Reset() |
| 516 | 516 |
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
|
| 517 | 517 |
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
|
| 518 |
+ if s.sendCompress != "" {
|
|
| 519 |
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
|
| 520 |
+ } |
|
| 518 | 521 |
p := http2.HeadersFrameParam{
|
| 519 | 522 |
StreamID: s.id, |
| 520 | 523 |
BlockFragment: t.hBuf.Bytes(), |
| ... | ... |
@@ -567,6 +592,10 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
|
| 567 | 567 |
// Got some quota. Try to acquire writing privilege on the |
| 568 | 568 |
// transport. |
| 569 | 569 |
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
|
| 570 |
+ if _, ok := err.(StreamError); ok {
|
|
| 571 |
+ // Return the connection quota back. |
|
| 572 |
+ t.sendQuotaPool.add(ps) |
|
| 573 |
+ } |
|
| 570 | 574 |
if t.framer.adjustNumWriters(-1) == 0 {
|
| 571 | 575 |
// This writer is the last one in this batch and has the |
| 572 | 576 |
// responsibility to flush the buffered frames. It queues |
| ... | ... |
@@ -576,6 +605,16 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
|
| 576 | 576 |
} |
| 577 | 577 |
return err |
| 578 | 578 |
} |
| 579 |
+ select {
|
|
| 580 |
+ case <-s.ctx.Done(): |
|
| 581 |
+ t.sendQuotaPool.add(ps) |
|
| 582 |
+ if t.framer.adjustNumWriters(-1) == 0 {
|
|
| 583 |
+ t.controlBuf.put(&flushIO{})
|
|
| 584 |
+ } |
|
| 585 |
+ t.writableChan <- 0 |
|
| 586 |
+ return ContextErr(s.ctx.Err()) |
|
| 587 |
+ default: |
|
| 588 |
+ } |
|
| 579 | 589 |
var forceFlush bool |
| 580 | 590 |
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last {
|
| 581 | 591 |
forceFlush = true |
| ... | ... |
@@ -673,20 +712,22 @@ func (t *http2Server) closeStream(s *Stream) {
|
| 673 | 673 |
t.mu.Lock() |
| 674 | 674 |
delete(t.activeStreams, s.id) |
| 675 | 675 |
t.mu.Unlock() |
| 676 |
- if q := s.fc.restoreConn(); q > 0 {
|
|
| 677 |
- t.controlBuf.put(&windowUpdate{0, q})
|
|
| 678 |
- } |
|
| 676 |
+ // In case stream sending and receiving are invoked in separate |
|
| 677 |
+ // goroutines (e.g., bi-directional streaming), cancel needs to be |
|
| 678 |
+ // called to interrupt the potential blocking on other goroutines. |
|
| 679 |
+ s.cancel() |
|
| 679 | 680 |
s.mu.Lock() |
| 681 |
+ if q := s.fc.resetPendingData(); q > 0 {
|
|
| 682 |
+ if w := t.fc.onRead(q); w > 0 {
|
|
| 683 |
+ t.controlBuf.put(&windowUpdate{0, w})
|
|
| 684 |
+ } |
|
| 685 |
+ } |
|
| 680 | 686 |
if s.state == streamDone {
|
| 681 | 687 |
s.mu.Unlock() |
| 682 | 688 |
return |
| 683 | 689 |
} |
| 684 | 690 |
s.state = streamDone |
| 685 | 691 |
s.mu.Unlock() |
| 686 |
- // In case stream sending and receiving are invoked in separate |
|
| 687 |
- // goroutines (e.g., bi-directional streaming), cancel needs to be |
|
| 688 |
- // called to interrupt the potential blocking on other goroutines. |
|
| 689 |
- s.cancel() |
|
| 690 | 692 |
} |
| 691 | 693 |
|
| 692 | 694 |
func (t *http2Server) RemoteAddr() net.Addr {
|
| ... | ... |
@@ -62,13 +62,14 @@ const ( |
| 62 | 62 |
) |
| 63 | 63 |
|
| 64 | 64 |
var ( |
| 65 |
- clientPreface = []byte(http2.ClientPreface) |
|
| 66 |
- http2RSTErrConvTab = map[http2.ErrCode]codes.Code{
|
|
| 65 |
+ clientPreface = []byte(http2.ClientPreface) |
|
| 66 |
+ http2ErrConvTab = map[http2.ErrCode]codes.Code{
|
|
| 67 | 67 |
http2.ErrCodeNo: codes.Internal, |
| 68 | 68 |
http2.ErrCodeProtocol: codes.Internal, |
| 69 | 69 |
http2.ErrCodeInternal: codes.Internal, |
| 70 | 70 |
http2.ErrCodeFlowControl: codes.ResourceExhausted, |
| 71 | 71 |
http2.ErrCodeSettingsTimeout: codes.Internal, |
| 72 |
+ http2.ErrCodeStreamClosed: codes.Internal, |
|
| 72 | 73 |
http2.ErrCodeFrameSize: codes.Internal, |
| 73 | 74 |
http2.ErrCodeRefusedStream: codes.Unavailable, |
| 74 | 75 |
http2.ErrCodeCancel: codes.Canceled, |
| ... | ... |
@@ -76,6 +77,7 @@ var ( |
| 76 | 76 |
http2.ErrCodeConnect: codes.Internal, |
| 77 | 77 |
http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted, |
| 78 | 78 |
http2.ErrCodeInadequateSecurity: codes.PermissionDenied, |
| 79 |
+ http2.ErrCodeHTTP11Required: codes.FailedPrecondition, |
|
| 79 | 80 |
} |
| 80 | 81 |
statusCodeConvTab = map[codes.Code]http2.ErrCode{
|
| 81 | 82 |
codes.Internal: http2.ErrCodeInternal, |
| ... | ... |
@@ -89,6 +91,9 @@ var ( |
| 89 | 89 |
// Records the states during HPACK decoding. Must be reset once the |
| 90 | 90 |
// decoding of the entire headers are finished. |
| 91 | 91 |
type decodeState struct {
|
| 92 |
+ err error // first error encountered decoding |
|
| 93 |
+ |
|
| 94 |
+ encoding string |
|
| 92 | 95 |
// statusCode caches the stream status received from the trailer |
| 93 | 96 |
// the server sent. Client side only. |
| 94 | 97 |
statusCode codes.Code |
| ... | ... |
@@ -101,25 +106,11 @@ type decodeState struct {
|
| 101 | 101 |
mdata map[string][]string |
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 |
-// An hpackDecoder decodes HTTP2 headers which may span multiple frames. |
|
| 105 |
-type hpackDecoder struct {
|
|
| 106 |
- h *hpack.Decoder |
|
| 107 |
- state decodeState |
|
| 108 |
- err error // The err when decoding |
|
| 109 |
-} |
|
| 110 |
- |
|
| 111 |
-// A headerFrame is either a http2.HeaderFrame or http2.ContinuationFrame. |
|
| 112 |
-type headerFrame interface {
|
|
| 113 |
- Header() http2.FrameHeader |
|
| 114 |
- HeaderBlockFragment() []byte |
|
| 115 |
- HeadersEnded() bool |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 | 104 |
// isReservedHeader checks whether hdr belongs to HTTP2 headers |
| 119 | 105 |
// reserved by gRPC protocol. Any other headers are classified as the |
| 120 | 106 |
// user-specified metadata. |
| 121 | 107 |
func isReservedHeader(hdr string) bool {
|
| 122 |
- if hdr[0] == ':' {
|
|
| 108 |
+ if hdr != "" && hdr[0] == ':' {
|
|
| 123 | 109 |
return true |
| 124 | 110 |
} |
| 125 | 111 |
switch hdr {
|
| ... | ... |
@@ -136,98 +127,62 @@ func isReservedHeader(hdr string) bool {
|
| 136 | 136 |
} |
| 137 | 137 |
} |
| 138 | 138 |
|
| 139 |
-func newHPACKDecoder() *hpackDecoder {
|
|
| 140 |
- d := &hpackDecoder{}
|
|
| 141 |
- d.h = hpack.NewDecoder(http2InitHeaderTableSize, func(f hpack.HeaderField) {
|
|
| 142 |
- switch f.Name {
|
|
| 143 |
- case "content-type": |
|
| 144 |
- if !strings.Contains(f.Value, "application/grpc") {
|
|
| 145 |
- d.err = StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected header") |
|
| 146 |
- return |
|
| 139 |
+func (d *decodeState) setErr(err error) {
|
|
| 140 |
+ if d.err == nil {
|
|
| 141 |
+ d.err = err |
|
| 142 |
+ } |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+func (d *decodeState) processHeaderField(f hpack.HeaderField) {
|
|
| 146 |
+ switch f.Name {
|
|
| 147 |
+ case "content-type": |
|
| 148 |
+ if !strings.Contains(f.Value, "application/grpc") {
|
|
| 149 |
+ d.setErr(StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value)) |
|
| 150 |
+ return |
|
| 151 |
+ } |
|
| 152 |
+ case "grpc-encoding": |
|
| 153 |
+ d.encoding = f.Value |
|
| 154 |
+ case "grpc-status": |
|
| 155 |
+ code, err := strconv.Atoi(f.Value) |
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ d.setErr(StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err)) |
|
| 158 |
+ return |
|
| 159 |
+ } |
|
| 160 |
+ d.statusCode = codes.Code(code) |
|
| 161 |
+ case "grpc-message": |
|
| 162 |
+ d.statusDesc = f.Value |
|
| 163 |
+ case "grpc-timeout": |
|
| 164 |
+ d.timeoutSet = true |
|
| 165 |
+ var err error |
|
| 166 |
+ d.timeout, err = timeoutDecode(f.Value) |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ d.setErr(StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err)) |
|
| 169 |
+ return |
|
| 170 |
+ } |
|
| 171 |
+ case ":path": |
|
| 172 |
+ d.method = f.Value |
|
| 173 |
+ default: |
|
| 174 |
+ if !isReservedHeader(f.Name) {
|
|
| 175 |
+ if f.Name == "user-agent" {
|
|
| 176 |
+ i := strings.LastIndex(f.Value, " ") |
|
| 177 |
+ if i == -1 {
|
|
| 178 |
+ // There is no application user agent string being set. |
|
| 179 |
+ return |
|
| 180 |
+ } |
|
| 181 |
+ // Extract the application user agent string. |
|
| 182 |
+ f.Value = f.Value[:i] |
|
| 147 | 183 |
} |
| 148 |
- case "grpc-status": |
|
| 149 |
- code, err := strconv.Atoi(f.Value) |
|
| 150 |
- if err != nil {
|
|
| 151 |
- d.err = StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err) |
|
| 152 |
- return |
|
| 184 |
+ if d.mdata == nil {
|
|
| 185 |
+ d.mdata = make(map[string][]string) |
|
| 153 | 186 |
} |
| 154 |
- d.state.statusCode = codes.Code(code) |
|
| 155 |
- case "grpc-message": |
|
| 156 |
- d.state.statusDesc = f.Value |
|
| 157 |
- case "grpc-timeout": |
|
| 158 |
- d.state.timeoutSet = true |
|
| 159 |
- var err error |
|
| 160 |
- d.state.timeout, err = timeoutDecode(f.Value) |
|
| 187 |
+ k, v, err := metadata.DecodeKeyValue(f.Name, f.Value) |
|
| 161 | 188 |
if err != nil {
|
| 162 |
- d.err = StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err) |
|
| 189 |
+ grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
|
|
| 163 | 190 |
return |
| 164 | 191 |
} |
| 165 |
- case ":path": |
|
| 166 |
- d.state.method = f.Value |
|
| 167 |
- default: |
|
| 168 |
- if !isReservedHeader(f.Name) {
|
|
| 169 |
- if f.Name == "user-agent" {
|
|
| 170 |
- i := strings.LastIndex(f.Value, " ") |
|
| 171 |
- if i == -1 {
|
|
| 172 |
- // There is no application user agent string being set. |
|
| 173 |
- return |
|
| 174 |
- } |
|
| 175 |
- // Extract the application user agent string. |
|
| 176 |
- f.Value = f.Value[:i] |
|
| 177 |
- } |
|
| 178 |
- if d.state.mdata == nil {
|
|
| 179 |
- d.state.mdata = make(map[string][]string) |
|
| 180 |
- } |
|
| 181 |
- k, v, err := metadata.DecodeKeyValue(f.Name, f.Value) |
|
| 182 |
- if err != nil {
|
|
| 183 |
- grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
|
|
| 184 |
- return |
|
| 185 |
- } |
|
| 186 |
- d.state.mdata[k] = append(d.state.mdata[k], v) |
|
| 187 |
- } |
|
| 192 |
+ d.mdata[k] = append(d.mdata[k], v) |
|
| 188 | 193 |
} |
| 189 |
- }) |
|
| 190 |
- return d |
|
| 191 |
-} |
|
| 192 |
- |
|
| 193 |
-func (d *hpackDecoder) decodeClientHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
|
|
| 194 |
- d.err = nil |
|
| 195 |
- _, err = d.h.Write(frame.HeaderBlockFragment()) |
|
| 196 |
- if err != nil {
|
|
| 197 |
- err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err) |
|
| 198 |
- } |
|
| 199 |
- |
|
| 200 |
- if frame.HeadersEnded() {
|
|
| 201 |
- if closeErr := d.h.Close(); closeErr != nil && err == nil {
|
|
| 202 |
- err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr) |
|
| 203 |
- } |
|
| 204 |
- endHeaders = true |
|
| 205 | 194 |
} |
| 206 |
- |
|
| 207 |
- if err == nil && d.err != nil {
|
|
| 208 |
- err = d.err |
|
| 209 |
- } |
|
| 210 |
- return |
|
| 211 |
-} |
|
| 212 |
- |
|
| 213 |
-func (d *hpackDecoder) decodeServerHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
|
|
| 214 |
- d.err = nil |
|
| 215 |
- _, err = d.h.Write(frame.HeaderBlockFragment()) |
|
| 216 |
- if err != nil {
|
|
| 217 |
- err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err) |
|
| 218 |
- } |
|
| 219 |
- |
|
| 220 |
- if frame.HeadersEnded() {
|
|
| 221 |
- if closeErr := d.h.Close(); closeErr != nil && err == nil {
|
|
| 222 |
- err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr) |
|
| 223 |
- } |
|
| 224 |
- endHeaders = true |
|
| 225 |
- } |
|
| 226 |
- |
|
| 227 |
- if err == nil && d.err != nil {
|
|
| 228 |
- err = d.err |
|
| 229 |
- } |
|
| 230 |
- return |
|
| 231 | 195 |
} |
| 232 | 196 |
|
| 233 | 197 |
type timeoutUnit uint8 |
| ... | ... |
@@ -318,10 +273,11 @@ type framer struct {
|
| 318 | 318 |
|
| 319 | 319 |
func newFramer(conn net.Conn) *framer {
|
| 320 | 320 |
f := &framer{
|
| 321 |
- reader: conn, |
|
| 321 |
+ reader: bufio.NewReaderSize(conn, http2IOBufSize), |
|
| 322 | 322 |
writer: bufio.NewWriterSize(conn, http2IOBufSize), |
| 323 | 323 |
} |
| 324 | 324 |
f.fr = http2.NewFramer(f.writer, f.reader) |
| 325 |
+ f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil) |
|
| 325 | 326 |
return f |
| 326 | 327 |
} |
| 327 | 328 |
|
| ... | ... |
@@ -449,3 +405,7 @@ func (f *framer) flushWrite() error {
|
| 449 | 449 |
func (f *framer) readFrame() (http2.Frame, error) {
|
| 450 | 450 |
return f.fr.ReadFrame() |
| 451 | 451 |
} |
| 452 |
+ |
|
| 453 |
+func (f *framer) errorDetail() error {
|
|
| 454 |
+ return f.fr.ErrorDetail() |
|
| 455 |
+} |
| ... | ... |
@@ -63,13 +63,11 @@ type recvMsg struct {
|
| 63 | 63 |
err error |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
-func (recvMsg) isItem() bool {
|
|
| 67 |
- return true |
|
| 68 |
-} |
|
| 66 |
+func (*recvMsg) item() {}
|
|
| 69 | 67 |
|
| 70 | 68 |
// All items in an out of a recvBuffer should be the same type. |
| 71 | 69 |
type item interface {
|
| 72 |
- isItem() bool |
|
| 70 |
+ item() |
|
| 73 | 71 |
} |
| 74 | 72 |
|
| 75 | 73 |
// recvBuffer is an unbounded channel of item. |
| ... | ... |
@@ -89,12 +87,14 @@ func newRecvBuffer() *recvBuffer {
|
| 89 | 89 |
func (b *recvBuffer) put(r item) {
|
| 90 | 90 |
b.mu.Lock() |
| 91 | 91 |
defer b.mu.Unlock() |
| 92 |
- b.backlog = append(b.backlog, r) |
|
| 93 |
- select {
|
|
| 94 |
- case b.c <- b.backlog[0]: |
|
| 95 |
- b.backlog = b.backlog[1:] |
|
| 96 |
- default: |
|
| 92 |
+ if len(b.backlog) == 0 {
|
|
| 93 |
+ select {
|
|
| 94 |
+ case b.c <- r: |
|
| 95 |
+ return |
|
| 96 |
+ default: |
|
| 97 |
+ } |
|
| 97 | 98 |
} |
| 99 |
+ b.backlog = append(b.backlog, r) |
|
| 98 | 100 |
} |
| 99 | 101 |
|
| 100 | 102 |
func (b *recvBuffer) load() {
|
| ... | ... |
@@ -170,11 +170,13 @@ type Stream struct {
|
| 170 | 170 |
ctx context.Context |
| 171 | 171 |
cancel context.CancelFunc |
| 172 | 172 |
// method records the associated RPC method of the stream. |
| 173 |
- method string |
|
| 174 |
- buf *recvBuffer |
|
| 175 |
- dec io.Reader |
|
| 176 |
- fc *inFlow |
|
| 177 |
- recvQuota uint32 |
|
| 173 |
+ method string |
|
| 174 |
+ recvCompress string |
|
| 175 |
+ sendCompress string |
|
| 176 |
+ buf *recvBuffer |
|
| 177 |
+ dec io.Reader |
|
| 178 |
+ fc *inFlow |
|
| 179 |
+ recvQuota uint32 |
|
| 178 | 180 |
// The accumulated inbound quota pending for window update. |
| 179 | 181 |
updateQuota uint32 |
| 180 | 182 |
// The handler to control the window update procedure for both this |
| ... | ... |
@@ -201,6 +203,17 @@ type Stream struct {
|
| 201 | 201 |
statusDesc string |
| 202 | 202 |
} |
| 203 | 203 |
|
| 204 |
+// RecvCompress returns the compression algorithm applied to the inbound |
|
| 205 |
+// message. It is empty string if there is no compression applied. |
|
| 206 |
+func (s *Stream) RecvCompress() string {
|
|
| 207 |
+ return s.recvCompress |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+// SetSendCompress sets the compression algorithm to the stream. |
|
| 211 |
+func (s *Stream) SetSendCompress(str string) {
|
|
| 212 |
+ s.sendCompress = str |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 204 | 215 |
// Header acquires the key-value pairs of header metadata once it |
| 205 | 216 |
// is available. It blocks until i) the metadata is ready or ii) there is no |
| 206 | 217 |
// header metadata or iii) the stream is cancelled/expired. |
| ... | ... |
@@ -286,20 +299,18 @@ func (s *Stream) Read(p []byte) (n int, err error) {
|
| 286 | 286 |
return |
| 287 | 287 |
} |
| 288 | 288 |
|
| 289 |
-type key int |
|
| 290 |
- |
|
| 291 | 289 |
// The key to save transport.Stream in the context. |
| 292 |
-const streamKey = key(0) |
|
| 290 |
+type streamKey struct{}
|
|
| 293 | 291 |
|
| 294 | 292 |
// newContextWithStream creates a new context from ctx and attaches stream |
| 295 | 293 |
// to it. |
| 296 | 294 |
func newContextWithStream(ctx context.Context, stream *Stream) context.Context {
|
| 297 |
- return context.WithValue(ctx, streamKey, stream) |
|
| 295 |
+ return context.WithValue(ctx, streamKey{}, stream)
|
|
| 298 | 296 |
} |
| 299 | 297 |
|
| 300 | 298 |
// StreamFromContext returns the stream saved in ctx. |
| 301 | 299 |
func StreamFromContext(ctx context.Context) (s *Stream, ok bool) {
|
| 302 |
- s, ok = ctx.Value(streamKey).(*Stream) |
|
| 300 |
+ s, ok = ctx.Value(streamKey{}).(*Stream)
|
|
| 303 | 301 |
return |
| 304 | 302 |
} |
| 305 | 303 |
|
| ... | ... |
@@ -339,20 +350,40 @@ func NewClientTransport(target string, opts *ConnectOptions) (ClientTransport, e |
| 339 | 339 |
// Options provides additional hints and information for message |
| 340 | 340 |
// transmission. |
| 341 | 341 |
type Options struct {
|
| 342 |
- // Indicate whether it is the last piece for this stream. |
|
| 342 |
+ // Last indicates whether this write is the last piece for |
|
| 343 |
+ // this stream. |
|
| 343 | 344 |
Last bool |
| 344 |
- // The hint to transport impl whether the data could be buffered for |
|
| 345 |
- // batching write. Transport impl can feel free to ignore it. |
|
| 345 |
+ |
|
| 346 |
+ // Delay is a hint to the transport implementation for whether |
|
| 347 |
+ // the data could be buffered for a batching write. The |
|
| 348 |
+ // Transport implementation may ignore the hint. |
|
| 346 | 349 |
Delay bool |
| 347 | 350 |
} |
| 348 | 351 |
|
| 349 | 352 |
// CallHdr carries the information of a particular RPC. |
| 350 | 353 |
type CallHdr struct {
|
| 351 |
- Host string // peer host |
|
| 352 |
- Method string // the operation to perform on the specified host |
|
| 354 |
+ // Host specifies the peer's host. |
|
| 355 |
+ Host string |
|
| 356 |
+ |
|
| 357 |
+ // Method specifies the operation to perform. |
|
| 358 |
+ Method string |
|
| 359 |
+ |
|
| 360 |
+ // RecvCompress specifies the compression algorithm applied on |
|
| 361 |
+ // inbound messages. |
|
| 362 |
+ RecvCompress string |
|
| 363 |
+ |
|
| 364 |
+ // SendCompress specifies the compression algorithm applied on |
|
| 365 |
+ // outbound message. |
|
| 366 |
+ SendCompress string |
|
| 367 |
+ |
|
| 368 |
+ // Flush indicates whether a new stream command should be sent |
|
| 369 |
+ // to the peer without waiting for the first data. This is |
|
| 370 |
+ // only a hint. The transport may modify the flush decision |
|
| 371 |
+ // for performance purposes. |
|
| 372 |
+ Flush bool |
|
| 353 | 373 |
} |
| 354 | 374 |
|
| 355 |
-// ClientTransport is the common interface for all gRPC client side transport |
|
| 375 |
+// ClientTransport is the common interface for all gRPC client-side transport |
|
| 356 | 376 |
// implementations. |
| 357 | 377 |
type ClientTransport interface {
|
| 358 | 378 |
// Close tears down this transport. Once it returns, the transport |
| ... | ... |
@@ -381,21 +412,33 @@ type ClientTransport interface {
|
| 381 | 381 |
Error() <-chan struct{}
|
| 382 | 382 |
} |
| 383 | 383 |
|
| 384 |
-// ServerTransport is the common interface for all gRPC server side transport |
|
| 384 |
+// ServerTransport is the common interface for all gRPC server-side transport |
|
| 385 | 385 |
// implementations. |
| 386 |
+// |
|
| 387 |
+// Methods may be called concurrently from multiple goroutines, but |
|
| 388 |
+// Write methods for a given Stream will be called serially. |
|
| 386 | 389 |
type ServerTransport interface {
|
| 387 |
- // WriteStatus sends the status of a stream to the client. |
|
| 388 |
- WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error |
|
| 389 |
- // Write sends the data for the given stream. |
|
| 390 |
- Write(s *Stream, data []byte, opts *Options) error |
|
| 391 |
- // WriteHeader sends the header metedata for the given stream. |
|
| 392 |
- WriteHeader(s *Stream, md metadata.MD) error |
|
| 393 | 390 |
// HandleStreams receives incoming streams using the given handler. |
| 394 | 391 |
HandleStreams(func(*Stream)) |
| 392 |
+ |
|
| 393 |
+ // WriteHeader sends the header metadata for the given stream. |
|
| 394 |
+ // WriteHeader may not be called on all streams. |
|
| 395 |
+ WriteHeader(s *Stream, md metadata.MD) error |
|
| 396 |
+ |
|
| 397 |
+ // Write sends the data for the given stream. |
|
| 398 |
+ // Write may not be called on all streams. |
|
| 399 |
+ Write(s *Stream, data []byte, opts *Options) error |
|
| 400 |
+ |
|
| 401 |
+ // WriteStatus sends the status of a stream to the client. |
|
| 402 |
+ // WriteStatus is the final call made on a stream and always |
|
| 403 |
+ // occurs. |
|
| 404 |
+ WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error |
|
| 405 |
+ |
|
| 395 | 406 |
// Close tears down the transport. Once it is called, the transport |
| 396 | 407 |
// should not be accessed any more. All the pending streams and their |
| 397 | 408 |
// handlers will be terminated asynchronously. |
| 398 | 409 |
Close() error |
| 410 |
+ |
|
| 399 | 411 |
// RemoteAddr returns the remote network address. |
| 400 | 412 |
RemoteAddr() net.Addr |
| 401 | 413 |
} |