|
@@ -682,12 +682,25 @@ func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func appendHeaderFieldsFromMD(headerFields []hpack.HeaderField, md metadata.MD) []hpack.HeaderField {
|
|
|
|
+ for k, vv := range md {
|
|
|
|
+ if isReservedHeader(k) {
|
|
|
|
+ // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ for _, v := range vv {
|
|
|
|
+ headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return headerFields
|
|
|
|
+}
|
|
|
|
+
|
|
// WriteHeader sends the header metedata md back to the client.
|
|
// WriteHeader sends the header metedata md back to the client.
|
|
func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|
func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|
- if s.headerOk || s.getState() == streamDone {
|
|
|
|
|
|
+ if s.updateHeaderSent() || s.getState() == streamDone {
|
|
return ErrIllegalHeaderWrite
|
|
return ErrIllegalHeaderWrite
|
|
}
|
|
}
|
|
- s.headerOk = true
|
|
|
|
|
|
+ s.hdrMu.Lock()
|
|
if md.Len() > 0 {
|
|
if md.Len() > 0 {
|
|
if s.header.Len() > 0 {
|
|
if s.header.Len() > 0 {
|
|
s.header = metadata.Join(s.header, md)
|
|
s.header = metadata.Join(s.header, md)
|
|
@@ -695,7 +708,12 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|
s.header = md
|
|
s.header = md
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- md = s.header
|
|
|
|
|
|
+ t.writeHeaderLocked(s)
|
|
|
|
+ s.hdrMu.Unlock()
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (t *http2Server) writeHeaderLocked(s *Stream) {
|
|
// TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
|
|
// TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
|
|
// first and create a slice of that exact size.
|
|
// first and create a slice of that exact size.
|
|
headerFields := make([]hpack.HeaderField, 0, 2) // at least :status, content-type will be there if none else.
|
|
headerFields := make([]hpack.HeaderField, 0, 2) // at least :status, content-type will be there if none else.
|
|
@@ -704,15 +722,7 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|
if s.sendCompress != "" {
|
|
if s.sendCompress != "" {
|
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
|
}
|
|
}
|
|
- for k, vv := range md {
|
|
|
|
- if isReservedHeader(k) {
|
|
|
|
- // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- for _, v := range vv {
|
|
|
|
- headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ headerFields = appendHeaderFieldsFromMD(headerFields, s.header)
|
|
t.controlBuf.put(&headerFrame{
|
|
t.controlBuf.put(&headerFrame{
|
|
streamID: s.id,
|
|
streamID: s.id,
|
|
hf: headerFields,
|
|
hf: headerFields,
|
|
@@ -728,7 +738,6 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|
outHeader := &stats.OutHeader{}
|
|
outHeader := &stats.OutHeader{}
|
|
t.stats.HandleRPC(s.Context(), outHeader)
|
|
t.stats.HandleRPC(s.Context(), outHeader)
|
|
}
|
|
}
|
|
- return nil
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// WriteStatus sends stream status to the client and terminates the stream.
|
|
// WriteStatus sends stream status to the client and terminates the stream.
|
|
@@ -736,21 +745,20 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|
// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
|
|
// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
|
|
// OK is adopted.
|
|
// OK is adopted.
|
|
func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
|
func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
|
- if !s.headerOk && s.header.Len() > 0 {
|
|
|
|
- if err := t.WriteHeader(s, nil); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- if s.getState() == streamDone {
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
|
|
+ if s.getState() == streamDone {
|
|
|
|
+ return nil
|
|
}
|
|
}
|
|
|
|
+ s.hdrMu.Lock()
|
|
// TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
|
|
// TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
|
|
// first and create a slice of that exact size.
|
|
// first and create a slice of that exact size.
|
|
headerFields := make([]hpack.HeaderField, 0, 2) // grpc-status and grpc-message will be there if none else.
|
|
headerFields := make([]hpack.HeaderField, 0, 2) // grpc-status and grpc-message will be there if none else.
|
|
- if !s.headerOk {
|
|
|
|
- headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
|
|
|
|
- headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)})
|
|
|
|
|
|
+ if !s.updateHeaderSent() { // No headers have been sent.
|
|
|
|
+ if len(s.header) > 0 { // Send a separate header frame.
|
|
|
|
+ t.writeHeaderLocked(s)
|
|
|
|
+ } else { // Send a trailer only response.
|
|
|
|
+ headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
|
|
|
|
+ headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)})
|
|
|
|
+ }
|
|
}
|
|
}
|
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))})
|
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))})
|
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())})
|
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())})
|
|
@@ -766,16 +774,8 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
|
}
|
|
}
|
|
|
|
|
|
// Attach the trailer metadata.
|
|
// Attach the trailer metadata.
|
|
- for k, vv := range s.trailer {
|
|
|
|
- // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
|
|
|
- if isReservedHeader(k) {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- for _, v := range vv {
|
|
|
|
- headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- trailer := &headerFrame{
|
|
|
|
|
|
+ headerFields = appendHeaderFieldsFromMD(headerFields, s.trailer)
|
|
|
|
+ trailingHeader := &headerFrame{
|
|
streamID: s.id,
|
|
streamID: s.id,
|
|
hf: headerFields,
|
|
hf: headerFields,
|
|
endStream: true,
|
|
endStream: true,
|
|
@@ -783,7 +783,8 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
|
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
|
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
|
},
|
|
},
|
|
}
|
|
}
|
|
- t.closeStream(s, false, 0, trailer, true)
|
|
|
|
|
|
+ s.hdrMu.Unlock()
|
|
|
|
+ t.closeStream(s, false, 0, trailingHeader, true)
|
|
if t.stats != nil {
|
|
if t.stats != nil {
|
|
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{})
|
|
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{})
|
|
}
|
|
}
|
|
@@ -793,7 +794,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
|
// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
|
|
// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
|
|
// is returns if it fails (e.g., framing error, transport error).
|
|
// is returns if it fails (e.g., framing error, transport error).
|
|
func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) error {
|
|
func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) error {
|
|
- if !s.headerOk { // Headers haven't been written yet.
|
|
|
|
|
|
+ if !s.isHeaderSent() { // Headers haven't been written yet.
|
|
if err := t.WriteHeader(s, nil); err != nil {
|
|
if err := t.WriteHeader(s, nil); err != nil {
|
|
// TODO(mmukhi, dfawley): Make sure this is the right code to return.
|
|
// TODO(mmukhi, dfawley): Make sure this is the right code to return.
|
|
return streamErrorf(codes.Internal, "transport: %v", err)
|
|
return streamErrorf(codes.Internal, "transport: %v", err)
|