OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // TODO(rtenhove) clean up frame buffer size calculations so that we aren't | 5 // TODO(rtenhove) clean up frame buffer size calculations so that we aren't |
6 // constantly adding and subtracting header sizes; this is ugly and error- | 6 // constantly adding and subtracting header sizes; this is ugly and error- |
7 // prone. | 7 // prone. |
8 | 8 |
9 #include "net/spdy/spdy_framer.h" | 9 #include "net/spdy/spdy_framer.h" |
10 | 10 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 #else | 74 #else |
75 #define CHANGE_STATE(newstate) \ | 75 #define CHANGE_STATE(newstate) \ |
76 do { \ | 76 do { \ |
77 DCHECK(state_ != SPDY_ERROR); \ | 77 DCHECK(state_ != SPDY_ERROR); \ |
78 DCHECK_EQ(previous_state_, state_); \ | 78 DCHECK_EQ(previous_state_, state_); \ |
79 previous_state_ = state_; \ | 79 previous_state_ = state_; \ |
80 state_ = newstate; \ | 80 state_ = newstate; \ |
81 } while (false) | 81 } while (false) |
82 #endif | 82 #endif |
83 | 83 |
84 SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(int version, | 84 SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat( |
85 uint32 wire) { | 85 SpdyMajorVersion version, uint32 wire) { |
86 if (version < 3) { | 86 if (version < SPDY3) { |
87 ConvertFlagsAndIdForSpdy2(&wire); | 87 ConvertFlagsAndIdForSpdy2(&wire); |
88 } | 88 } |
89 return SettingsFlagsAndId(ntohl(wire) >> 24, ntohl(wire) & 0x00ffffff); | 89 return SettingsFlagsAndId(ntohl(wire) >> 24, ntohl(wire) & 0x00ffffff); |
90 } | 90 } |
91 | 91 |
92 SettingsFlagsAndId::SettingsFlagsAndId(uint8 flags, uint32 id) | 92 SettingsFlagsAndId::SettingsFlagsAndId(uint8 flags, uint32 id) |
93 : flags_(flags), id_(id & 0x00ffffff) { | 93 : flags_(flags), id_(id & 0x00ffffff) { |
94 LOG_IF(DFATAL, id > (1u << 24)) << "SPDY setting ID too large: " << id; | 94 LOG_IF(DFATAL, id > (1u << 24)) << "SPDY setting ID too large: " << id; |
95 } | 95 } |
96 | 96 |
97 uint32 SettingsFlagsAndId::GetWireFormat(int version) const { | 97 uint32 SettingsFlagsAndId::GetWireFormat(SpdyMajorVersion version) |
| 98 const { |
98 uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24); | 99 uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24); |
99 if (version < 3) { | 100 if (version < SPDY3) { |
100 ConvertFlagsAndIdForSpdy2(&wire); | 101 ConvertFlagsAndIdForSpdy2(&wire); |
101 } | 102 } |
102 return wire; | 103 return wire; |
103 } | 104 } |
104 | 105 |
105 // SPDY 2 had a bug in it with respect to byte ordering of id/flags field. | 106 // SPDY 2 had a bug in it with respect to byte ordering of id/flags field. |
106 // This method is used to preserve buggy behavior and works on both | 107 // This method is used to preserve buggy behavior and works on both |
107 // little-endian and big-endian hosts. | 108 // little-endian and big-endian hosts. |
108 // This method is also bidirectional (can be used to translate SPDY 2 to SPDY 3 | 109 // This method is also bidirectional (can be used to translate SPDY 2 to SPDY 3 |
109 // as well as vice versa). | 110 // as well as vice versa). |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 | 248 |
248 size_t SpdyFramer::GetGoAwayMinimumSize() const { | 249 size_t SpdyFramer::GetGoAwayMinimumSize() const { |
249 // Size, in bytes, of this GOAWAY frame. Calculated as: | 250 // Size, in bytes, of this GOAWAY frame. Calculated as: |
250 // 1. Control frame header size | 251 // 1. Control frame header size |
251 size_t size = GetControlFrameHeaderSize(); | 252 size_t size = GetControlFrameHeaderSize(); |
252 | 253 |
253 // 2. Last good stream id (4 bytes) | 254 // 2. Last good stream id (4 bytes) |
254 size += 4; | 255 size += 4; |
255 | 256 |
256 // 3. SPDY 3+ GOAWAY frames also contain a status (4 bytes) | 257 // 3. SPDY 3+ GOAWAY frames also contain a status (4 bytes) |
257 if (protocol_version() >= 3) { | 258 if (protocol_version() >= SPDY3) { |
258 size += 4; | 259 size += 4; |
259 } | 260 } |
260 | 261 |
261 return size; | 262 return size; |
262 } | 263 } |
263 | 264 |
264 size_t SpdyFramer::GetHeadersMinimumSize() const { | 265 size_t SpdyFramer::GetHeadersMinimumSize() const { |
265 // Size, in bytes, of a HEADERS frame not including the variable-length | 266 // Size, in bytes, of a HEADERS frame not including the variable-length |
266 // name-value block. | 267 // name-value block. |
267 size_t size = GetControlFrameHeaderSize(); | 268 size_t size = GetControlFrameHeaderSize(); |
(...skipping 18 matching lines...) Expand all Loading... |
286 // control frame header + 4 (stream id) + 4 (delta) | 287 // control frame header + 4 (stream id) + 4 (delta) |
287 return GetControlFrameHeaderSize() + 8; | 288 return GetControlFrameHeaderSize() + 8; |
288 } else { | 289 } else { |
289 // Calculated as: | 290 // Calculated as: |
290 // frame prefix + 4 (delta) | 291 // frame prefix + 4 (delta) |
291 return GetControlFrameHeaderSize() + 4; | 292 return GetControlFrameHeaderSize() + 4; |
292 } | 293 } |
293 } | 294 } |
294 | 295 |
295 size_t SpdyFramer::GetBlockedSize() const { | 296 size_t SpdyFramer::GetBlockedSize() const { |
296 DCHECK_LE(4, protocol_version()); | 297 DCHECK_LT(SPDY3, protocol_version()); |
297 // Size, in bytes, of a BLOCKED frame. | 298 // Size, in bytes, of a BLOCKED frame. |
298 // The BLOCKED frame has no payload beyond the control frame header. | 299 // The BLOCKED frame has no payload beyond the control frame header. |
299 return GetControlFrameHeaderSize(); | 300 return GetControlFrameHeaderSize(); |
300 } | 301 } |
301 | 302 |
302 size_t SpdyFramer::GetPushPromiseMinimumSize() const { | 303 size_t SpdyFramer::GetPushPromiseMinimumSize() const { |
303 DCHECK_LE(4, protocol_version()); | 304 DCHECK_LT(SPDY3, protocol_version()); |
304 // Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block. | 305 // Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block. |
305 // Calculated as frame prefix + 4 (promised stream id). | 306 // Calculated as frame prefix + 4 (promised stream id). |
306 return GetControlFrameHeaderSize() + 4; | 307 return GetControlFrameHeaderSize() + 4; |
307 } | 308 } |
308 | 309 |
309 size_t SpdyFramer::GetContinuationMinimumSize() const { | 310 size_t SpdyFramer::GetContinuationMinimumSize() const { |
310 // Size, in bytes, of a CONTINUATION frame not including the variable-length | 311 // Size, in bytes, of a CONTINUATION frame not including the variable-length |
311 // headers fragments. | 312 // headers fragments. |
312 return GetControlFrameHeaderSize(); | 313 return GetControlFrameHeaderSize(); |
313 } | 314 } |
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 | 631 |
631 uint16 control_frame_type_field = DATA; | 632 uint16 control_frame_type_field = DATA; |
632 // ProcessControlFrameHeader() will set current_frame_type_ to the | 633 // ProcessControlFrameHeader() will set current_frame_type_ to the |
633 // correct value if this is a valid control frame. | 634 // correct value if this is a valid control frame. |
634 current_frame_type_ = DATA; | 635 current_frame_type_ = DATA; |
635 if (protocol_version() <= SPDY3) { | 636 if (protocol_version() <= SPDY3) { |
636 bool successful_read = reader->ReadUInt16(&version); | 637 bool successful_read = reader->ReadUInt16(&version); |
637 DCHECK(successful_read); | 638 DCHECK(successful_read); |
638 is_control_frame = (version & kControlFlagMask) != 0; | 639 is_control_frame = (version & kControlFlagMask) != 0; |
639 version &= ~kControlFlagMask; // Only valid for control frames. | 640 version &= ~kControlFlagMask; // Only valid for control frames. |
| 641 if (is_control_frame && |
| 642 version >= SpdyConstants::SerializeMajorVersion(SPDY_MIN_VERSION) && |
| 643 version <= SpdyConstants::SerializeMajorVersion(SPDY_MAX_VERSION)) { |
| 644 version = SpdyConstants::ParseMajorVersion(version); |
| 645 } |
640 | 646 |
641 if (is_control_frame) { | 647 if (is_control_frame) { |
642 // We check control_frame_type_field's validity in | 648 // We check control_frame_type_field's validity in |
643 // ProcessControlFrameHeader(). | 649 // ProcessControlFrameHeader(). |
644 successful_read = reader->ReadUInt16(&control_frame_type_field); | 650 successful_read = reader->ReadUInt16(&control_frame_type_field); |
645 } else { | 651 } else { |
646 reader->Rewind(); | 652 reader->Rewind(); |
647 successful_read = reader->ReadUInt31(¤t_frame_stream_id_); | 653 successful_read = reader->ReadUInt31(¤t_frame_stream_id_); |
648 } | 654 } |
649 DCHECK(successful_read); | 655 DCHECK(successful_read); |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
750 if (current_frame_flags_ & DATA_FLAG_FIN) { | 756 if (current_frame_flags_ & DATA_FLAG_FIN) { |
751 visitor_->OnStreamFrameData( | 757 visitor_->OnStreamFrameData( |
752 current_frame_stream_id_, NULL, 0, true); | 758 current_frame_stream_id_, NULL, 0, true); |
753 } | 759 } |
754 CHANGE_STATE(SPDY_AUTO_RESET); | 760 CHANGE_STATE(SPDY_AUTO_RESET); |
755 } | 761 } |
756 } | 762 } |
757 } else if (version != protocol_version()) { | 763 } else if (version != protocol_version()) { |
758 // We check version before we check validity: version can never be | 764 // We check version before we check validity: version can never be |
759 // 'invalid', it can only be unsupported. | 765 // 'invalid', it can only be unsupported. |
760 DVLOG(1) << "Unsupported SPDY version " << version | 766 DVLOG(1) << "Unsupported SPDY version " |
| 767 << version |
761 << " (expected " << protocol_version() << ")"; | 768 << " (expected " << protocol_version() << ")"; |
762 set_error(SPDY_UNSUPPORTED_VERSION); | 769 set_error(SPDY_UNSUPPORTED_VERSION); |
763 } else { | 770 } else { |
764 ProcessControlFrameHeader(control_frame_type_field); | 771 ProcessControlFrameHeader(control_frame_type_field); |
765 } | 772 } |
766 | 773 |
767 return original_len - len; | 774 return original_len - len; |
768 } | 775 } |
769 | 776 |
770 void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { | 777 void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
918 if (current_frame_length_ < GetPushPromiseMinimumSize()) { | 925 if (current_frame_length_ < GetPushPromiseMinimumSize()) { |
919 set_error(SPDY_INVALID_CONTROL_FRAME); | 926 set_error(SPDY_INVALID_CONTROL_FRAME); |
920 } else if (protocol_version() <= SPDY3 && current_frame_flags_ != 0) { | 927 } else if (protocol_version() <= SPDY3 && current_frame_flags_ != 0) { |
921 set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); | 928 set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
922 } else if (protocol_version() > SPDY3 && current_frame_flags_ & | 929 } else if (protocol_version() > SPDY3 && current_frame_flags_ & |
923 ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE) { | 930 ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE) { |
924 set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); | 931 set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
925 } | 932 } |
926 break; | 933 break; |
927 case CONTINUATION: | 934 case CONTINUATION: |
928 if (current_frame_length_ < GetContinuationMinimumSize()) { | 935 if (current_frame_length_ < GetContinuationMinimumSize() || |
| 936 protocol_version() <= SPDY3) { |
929 set_error(SPDY_INVALID_CONTROL_FRAME); | 937 set_error(SPDY_INVALID_CONTROL_FRAME); |
930 } else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) { | 938 } else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) { |
931 set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); | 939 set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
932 } | 940 } |
933 break; | 941 break; |
934 default: | 942 default: |
935 LOG(WARNING) << "Valid " << display_protocol_ | 943 LOG(WARNING) << "Valid " << display_protocol_ |
936 << " control frame with unhandled type: " | 944 << " control frame with unhandled type: " |
937 << current_frame_type_; | 945 << current_frame_type_; |
938 // This branch should be unreachable because of the frame type bounds | 946 // This branch should be unreachable because of the frame type bounds |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1034 memcpy(current_frame_buffer_.get() + current_frame_buffer_length_, | 1042 memcpy(current_frame_buffer_.get() + current_frame_buffer_length_, |
1035 *data, | 1043 *data, |
1036 bytes_to_read); | 1044 bytes_to_read); |
1037 current_frame_buffer_length_ += bytes_to_read; | 1045 current_frame_buffer_length_ += bytes_to_read; |
1038 *data += bytes_to_read; | 1046 *data += bytes_to_read; |
1039 *len -= bytes_to_read; | 1047 *len -= bytes_to_read; |
1040 } | 1048 } |
1041 return bytes_to_read; | 1049 return bytes_to_read; |
1042 } | 1050 } |
1043 | 1051 |
1044 size_t SpdyFramer::GetSerializedLength(const int spdy_version, | 1052 size_t SpdyFramer::GetSerializedLength( |
1045 const SpdyHeaderBlock* headers) { | 1053 const SpdyMajorVersion spdy_version, |
| 1054 const SpdyHeaderBlock* headers) { |
1046 const size_t num_name_value_pairs_size | 1055 const size_t num_name_value_pairs_size |
1047 = (spdy_version < 3) ? sizeof(uint16) : sizeof(uint32); | 1056 = (spdy_version < SPDY3) ? sizeof(uint16) : sizeof(uint32); |
1048 const size_t length_of_name_size = num_name_value_pairs_size; | 1057 const size_t length_of_name_size = num_name_value_pairs_size; |
1049 const size_t length_of_value_size = num_name_value_pairs_size; | 1058 const size_t length_of_value_size = num_name_value_pairs_size; |
1050 | 1059 |
1051 size_t total_length = num_name_value_pairs_size; | 1060 size_t total_length = num_name_value_pairs_size; |
1052 for (SpdyHeaderBlock::const_iterator it = headers->begin(); | 1061 for (SpdyHeaderBlock::const_iterator it = headers->begin(); |
1053 it != headers->end(); | 1062 it != headers->end(); |
1054 ++it) { | 1063 ++it) { |
1055 // We add space for the length of the name and the length of the value as | 1064 // We add space for the length of the name and the length of the value as |
1056 // well as the length of the name and the length of the value. | 1065 // well as the length of the name and the length of the value. |
1057 total_length += length_of_name_size + it->first.size() + | 1066 total_length += length_of_name_size + it->first.size() + |
1058 length_of_value_size + it->second.size(); | 1067 length_of_value_size + it->second.size(); |
1059 } | 1068 } |
1060 return total_length; | 1069 return total_length; |
1061 } | 1070 } |
1062 | 1071 |
1063 void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, | 1072 void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, |
1064 const int spdy_version, | 1073 const SpdyMajorVersion spdy_version, |
1065 const SpdyHeaderBlock* headers) { | 1074 const SpdyHeaderBlock* headers) { |
1066 if (spdy_version < 3) { | 1075 if (spdy_version < SPDY3) { |
1067 frame->WriteUInt16(headers->size()); // Number of headers. | 1076 frame->WriteUInt16(headers->size()); // Number of headers. |
1068 } else { | 1077 } else { |
1069 frame->WriteUInt32(headers->size()); // Number of headers. | 1078 frame->WriteUInt32(headers->size()); // Number of headers. |
1070 } | 1079 } |
1071 SpdyHeaderBlock::const_iterator it; | 1080 SpdyHeaderBlock::const_iterator it; |
1072 for (it = headers->begin(); it != headers->end(); ++it) { | 1081 for (it = headers->begin(); it != headers->end(); ++it) { |
1073 if (spdy_version < 3) { | 1082 if (spdy_version < SPDY3) { |
1074 frame->WriteString(it->first); | 1083 frame->WriteString(it->first); |
1075 frame->WriteString(it->second); | 1084 frame->WriteString(it->second); |
1076 } else { | 1085 } else { |
1077 frame->WriteStringPiece32(it->first); | 1086 frame->WriteStringPiece32(it->first); |
1078 frame->WriteStringPiece32(it->second); | 1087 frame->WriteStringPiece32(it->second); |
1079 } | 1088 } |
1080 } | 1089 } |
1081 } | 1090 } |
1082 | 1091 |
1083 // TODO(phajdan.jr): Clean up after we no longer need | 1092 // TODO(phajdan.jr): Clean up after we no longer need |
(...skipping 775 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1859 } | 1868 } |
1860 | 1869 |
1861 size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { | 1870 size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { |
1862 DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_); | 1871 DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_); |
1863 | 1872 |
1864 size_t original_len = len; | 1873 size_t original_len = len; |
1865 if (remaining_padding_length_fields_ == 0) { | 1874 if (remaining_padding_length_fields_ == 0) { |
1866 DCHECK_EQ(remaining_padding_payload_length_, 0u); | 1875 DCHECK_EQ(remaining_padding_payload_length_, 0u); |
1867 bool pad_low = false; | 1876 bool pad_low = false; |
1868 bool pad_high = false; | 1877 bool pad_high = false; |
1869 if (current_frame_flags_ & net::DATA_FLAG_PAD_LOW) { | 1878 if (current_frame_flags_ & DATA_FLAG_PAD_LOW) { |
1870 pad_low = true; | 1879 pad_low = true; |
1871 ++remaining_padding_length_fields_; | 1880 ++remaining_padding_length_fields_; |
1872 } | 1881 } |
1873 if (current_frame_flags_ & net::DATA_FLAG_PAD_HIGH) { | 1882 if (current_frame_flags_ & DATA_FLAG_PAD_HIGH) { |
1874 pad_high = true; | 1883 pad_high = true; |
1875 ++remaining_padding_length_fields_; | 1884 ++remaining_padding_length_fields_; |
1876 } | 1885 } |
1877 if ((pad_high && !pad_low) || | 1886 if ((pad_high && !pad_low) || |
1878 remaining_data_length_ < remaining_padding_length_fields_) { | 1887 remaining_data_length_ < remaining_padding_length_fields_) { |
1879 set_error(SPDY_INVALID_DATA_FRAME_FLAGS); | 1888 set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
1880 return 0; | 1889 return 0; |
1881 } | 1890 } |
1882 } | 1891 } |
1883 | 1892 |
(...skipping 787 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2671 return uncompressed_length; | 2680 return uncompressed_length; |
2672 } | 2681 } |
2673 z_stream* compressor = GetHeaderCompressor(); | 2682 z_stream* compressor = GetHeaderCompressor(); |
2674 // Since we'll be performing lots of flushes when compressing the data, | 2683 // Since we'll be performing lots of flushes when compressing the data, |
2675 // zlib's lower bounds may be insufficient. | 2684 // zlib's lower bounds may be insufficient. |
2676 return 2 * deflateBound(compressor, uncompressed_length); | 2685 return 2 * deflateBound(compressor, uncompressed_length); |
2677 } | 2686 } |
2678 | 2687 |
2679 size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { | 2688 size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { |
2680 const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); | 2689 const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); |
2681 DCHECK_GT(protocol_version(), net::SPDY3); | 2690 DCHECK_GT(protocol_version(), SPDY3); |
2682 DCHECK_GT(size, kMaxControlFrameSize); | 2691 DCHECK_GT(size, kMaxControlFrameSize); |
2683 size_t overflow = size - kMaxControlFrameSize; | 2692 size_t overflow = size - kMaxControlFrameSize; |
2684 return overflow / (kMaxControlFrameSize - GetContinuationMinimumSize()) + 1; | 2693 return overflow / (kMaxControlFrameSize - GetContinuationMinimumSize()) + 1; |
2685 } | 2694 } |
2686 | 2695 |
2687 void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, | 2696 void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, |
2688 const string& hpack_encoding, | 2697 const string& hpack_encoding, |
2689 SpdyStreamId stream_id, | 2698 SpdyStreamId stream_id, |
2690 SpdyFrameType type) { | 2699 SpdyFrameType type) { |
2691 const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); | 2700 const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2981 builder->Seek(compressed_size); | 2990 builder->Seek(compressed_size); |
2982 builder->RewriteLength(*this); | 2991 builder->RewriteLength(*this); |
2983 | 2992 |
2984 pre_compress_bytes.Add(uncompressed_len); | 2993 pre_compress_bytes.Add(uncompressed_len); |
2985 post_compress_bytes.Add(compressed_size); | 2994 post_compress_bytes.Add(compressed_size); |
2986 | 2995 |
2987 compressed_frames.Increment(); | 2996 compressed_frames.Increment(); |
2988 } | 2997 } |
2989 | 2998 |
2990 } // namespace net | 2999 } // namespace net |
OLD | NEW |