From ec2eb8bc4804f9b880f256a55e3cbfc0923b6a29 Mon Sep 17 00:00:00 2001
From: Chris Lu <chris.lu@gmail.com>
Date: Wed, 8 Apr 2020 08:12:00 -0700
Subject: [PATCH] add If-None-Match and If-Modified-Since

fix https://github.com/chrislusf/seaweedfs/issues/1269
---
 other/java/client/src/main/proto/filer.proto  |   1 +
 weed/filer2/entry.go                          |   1 +
 weed/filer2/entry_codec.go                    |   6 +
 weed/filer2/filechunks.go                     |  16 +-
 weed/pb/filer.proto                           |   1 +
 weed/pb/filer_pb/filer.pb.go                  | 246 +++++++++---------
 weed/replication/sink/filersink/filer_sink.go |   4 +-
 weed/s3api/filer_multipart.go                 |   4 +-
 weed/s3api/s3api_objects_list_handlers.go     |   2 +-
 weed/server/filer_server_handlers_read.go     |  21 +-
 weed/server/filer_server_handlers_write.go    |  18 +-
 .../filer_server_handlers_write_autochunk.go  |   8 +-
 12 files changed, 193 insertions(+), 135 deletions(-)

diff --git a/other/java/client/src/main/proto/filer.proto b/other/java/client/src/main/proto/filer.proto
index e504e4f84..8f8176ff4 100644
--- a/other/java/client/src/main/proto/filer.proto
+++ b/other/java/client/src/main/proto/filer.proto
@@ -123,6 +123,7 @@ message FuseAttributes {
     string user_name = 11; // for hdfs
     repeated string group_name = 12; // for hdfs
     string symlink_target = 13;
+    bytes md5 = 14;
 }
 
 message CreateEntryRequest {
diff --git a/weed/filer2/entry.go b/weed/filer2/entry.go
index ef6c8f9a6..6dff99af9 100644
--- a/weed/filer2/entry.go
+++ b/weed/filer2/entry.go
@@ -21,6 +21,7 @@ type Attr struct {
 	UserName      string
 	GroupNames    []string
 	SymlinkTarget string
+	Md5           []byte
 }
 
 func (attr Attr) IsDirectory() bool {
diff --git a/weed/filer2/entry_codec.go b/weed/filer2/entry_codec.go
index 3a2dc6134..47c911011 100644
--- a/weed/filer2/entry_codec.go
+++ b/weed/filer2/entry_codec.go
@@ -52,6 +52,7 @@ func EntryAttributeToPb(entry *Entry) *filer_pb.FuseAttributes {
 		UserName:      entry.Attr.UserName,
 		GroupName:     entry.Attr.GroupNames,
 		SymlinkTarget: entry.Attr.SymlinkTarget,
+		Md5:           entry.Attr.Md5,
 	}
 }
 
@@ -71,6 +72,7 @@ func PbToEntryAttribute(attr *filer_pb.FuseAttributes) Attr {
 	t.UserName = attr.UserName
 	t.GroupNames = attr.GroupName
 	t.SymlinkTarget = attr.SymlinkTarget
+	t.Md5 = attr.Md5
 
 	return t
 }
@@ -93,6 +95,10 @@ func EqualEntry(a, b *Entry) bool {
 		return false
 	}
 
+	if !bytes.Equal(a.Md5, b.Md5) {
+		return false
+	}
+
 	for i := 0; i < len(a.Chunks); i++ {
 		if !proto.Equal(a.Chunks[i], b.Chunks[i]) {
 			return false
diff --git a/weed/filer2/filechunks.go b/weed/filer2/filechunks.go
index 0c93c389b..e8e4c305b 100644
--- a/weed/filer2/filechunks.go
+++ b/weed/filer2/filechunks.go
@@ -20,7 +20,21 @@ func TotalSize(chunks []*filer_pb.FileChunk) (size uint64) {
 	return
 }
 
-func ETag(chunks []*filer_pb.FileChunk) (etag string) {
+func ETag(entry *filer_pb.Entry) (etag string) {
+	if entry.Attributes == nil || entry.Attributes.Md5 == nil {
+		ETagChunks(entry.Chunks)
+	}
+	return fmt.Sprintf("%x", entry.Attributes.Md5)
+}
+
+func ETagEntry(entry *Entry) (etag string) {
+	if entry.Attr.Md5 == nil {
+		ETagChunks(entry.Chunks)
+	}
+	return fmt.Sprintf("%x", entry.Attr.Md5)
+}
+
+func ETagChunks(chunks []*filer_pb.FileChunk) (etag string) {
 	if len(chunks) == 1 {
 		return chunks[0].ETag
 	}
diff --git a/weed/pb/filer.proto b/weed/pb/filer.proto
index e504e4f84..8f8176ff4 100644
--- a/weed/pb/filer.proto
+++ b/weed/pb/filer.proto
@@ -123,6 +123,7 @@ message FuseAttributes {
     string user_name = 11; // for hdfs
     repeated string group_name = 12; // for hdfs
     string symlink_target = 13;
+    bytes md5 = 14;
 }
 
 message CreateEntryRequest {
diff --git a/weed/pb/filer_pb/filer.pb.go b/weed/pb/filer_pb/filer.pb.go
index cd1f657af..814856593 100644
--- a/weed/pb/filer_pb/filer.pb.go
+++ b/weed/pb/filer_pb/filer.pb.go
@@ -415,6 +415,7 @@ type FuseAttributes struct {
 	UserName      string   `protobuf:"bytes,11,opt,name=user_name,json=userName" json:"user_name,omitempty"`
 	GroupName     []string `protobuf:"bytes,12,rep,name=group_name,json=groupName" json:"group_name,omitempty"`
 	SymlinkTarget string   `protobuf:"bytes,13,opt,name=symlink_target,json=symlinkTarget" json:"symlink_target,omitempty"`
+	Md5           []byte   `protobuf:"bytes,14,opt,name=md5,proto3" json:"md5,omitempty"`
 }
 
 func (m *FuseAttributes) Reset()                    { *m = FuseAttributes{} }
@@ -513,6 +514,13 @@ func (m *FuseAttributes) GetSymlinkTarget() string {
 	return ""
 }
 
+func (m *FuseAttributes) GetMd5() []byte {
+	if m != nil {
+		return m.Md5
+	}
+	return nil
+}
+
 type CreateEntryRequest struct {
 	Directory string `protobuf:"bytes,1,opt,name=directory" json:"directory,omitempty"`
 	Entry     *Entry `protobuf:"bytes,2,opt,name=entry" json:"entry,omitempty"`
@@ -1707,125 +1715,125 @@ var _SeaweedFiler_serviceDesc = grpc.ServiceDesc{
 func init() { proto.RegisterFile("filer.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-	// 1909 bytes of a gzipped FileDescriptorProto
+	// 1919 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x58, 0x5f, 0x6f, 0xdc, 0xc6,
 	0x11, 0x37, 0xef, 0x74, 0x7f, 0x38, 0x77, 0x67, 0x4b, 0x7b, 0xb2, 0x73, 0x3e, 0x4b, 0xb6, 0x42,
-	0xd7, 0xa9, 0x0b, 0x1b, 0xaa, 0xa1, 0xe6, 0x21, 0x69, 0xda, 0x07, 0x5b, 0x96, 0x52, 0x37, 0xb6,
-	0xe2, 0x52, 0x76, 0x91, 0xa2, 0x40, 0x09, 0x8a, 0x5c, 0xdd, 0x6d, 0xc5, 0x23, 0x99, 0xdd, 0xa5,
-	0xfe, 0xe4, 0xad, 0x5f, 0xa3, 0x40, 0x1f, 0xfa, 0x1d, 0xfa, 0x58, 0xf4, 0xa5, 0x28, 0xd0, 0xcf,
-	0xd1, 0xc7, 0x3e, 0xf4, 0x33, 0x14, 0x3b, 0x4b, 0xf2, 0x96, 0xc7, 0x93, 0x94, 0x20, 0xc8, 0x1b,
-	0x77, 0x66, 0x76, 0x76, 0x76, 0xfe, 0xfc, 0x66, 0x96, 0xd0, 0x3b, 0x66, 0x11, 0xe5, 0xdb, 0x29,
-	0x4f, 0x64, 0x42, 0xba, 0xb8, 0xf0, 0xd2, 0x23, 0xe7, 0x4b, 0xb8, 0xf7, 0x3a, 0x49, 0x4e, 0xb2,
-	0xf4, 0x25, 0xe3, 0x34, 0x90, 0x09, 0xbf, 0xd8, 0x8b, 0x25, 0xbf, 0x70, 0xe9, 0xd7, 0x19, 0x15,
-	0x92, 0x6c, 0x80, 0x1d, 0x16, 0x8c, 0x91, 0xb5, 0x65, 0x3d, 0xb6, 0xdd, 0x39, 0x81, 0x10, 0x58,
-	0x89, 0xfd, 0x19, 0x1d, 0x35, 0x90, 0x81, 0xdf, 0xce, 0x1e, 0x6c, 0x2c, 0x57, 0x28, 0xd2, 0x24,
-	0x16, 0x94, 0x3c, 0x82, 0x16, 0x55, 0x04, 0xd4, 0xd6, 0xdb, 0xb9, 0xb5, 0x5d, 0x98, 0xb2, 0xad,
-	0xe5, 0x34, 0xd7, 0xf9, 0x87, 0x05, 0xe4, 0x35, 0x13, 0x52, 0x11, 0x19, 0x15, 0xdf, 0xce, 0x9e,
-	0x3b, 0xd0, 0x4e, 0x39, 0x3d, 0x66, 0xe7, 0xb9, 0x45, 0xf9, 0x8a, 0x3c, 0x85, 0x35, 0x21, 0x7d,
-	0x2e, 0xf7, 0x79, 0x32, 0xdb, 0x67, 0x11, 0x3d, 0x50, 0x46, 0x37, 0x51, 0xa4, 0xce, 0x20, 0xdb,
-	0x40, 0x58, 0x1c, 0x44, 0x99, 0x60, 0xa7, 0xf4, 0xb0, 0xe0, 0x8e, 0x56, 0xb6, 0xac, 0xc7, 0x5d,
-	0x77, 0x09, 0x87, 0xac, 0x43, 0x2b, 0x62, 0x33, 0x26, 0x47, 0xad, 0x2d, 0xeb, 0xf1, 0xc0, 0xd5,
-	0x0b, 0xe7, 0x17, 0x30, 0xac, 0xd8, 0xff, 0xdd, 0xae, 0xff, 0x97, 0x06, 0xb4, 0x90, 0x50, 0xfa,
-	0xd8, 0x9a, 0xfb, 0x98, 0x7c, 0x08, 0x7d, 0x26, 0xbc, 0xb9, 0x23, 0x1a, 0x68, 0x5b, 0x8f, 0x89,
-	0xd2, 0xe7, 0xe4, 0x09, 0xb4, 0x83, 0x69, 0x16, 0x9f, 0x88, 0x51, 0x73, 0xab, 0xf9, 0xb8, 0xb7,
-	0x33, 0x9c, 0x1f, 0xa4, 0x2e, 0xba, 0xab, 0x78, 0x6e, 0x2e, 0x42, 0x3e, 0x01, 0xf0, 0xa5, 0xe4,
-	0xec, 0x28, 0x93, 0x54, 0xe0, 0x4d, 0x7b, 0x3b, 0x23, 0x63, 0x43, 0x26, 0xe8, 0xf3, 0x92, 0xef,
-	0x1a, 0xb2, 0xe4, 0x53, 0xe8, 0xd2, 0x73, 0x49, 0xe3, 0x90, 0x86, 0xa3, 0x16, 0x1e, 0xb4, 0xb9,
-	0x70, 0xa3, 0xed, 0xbd, 0x9c, 0xaf, 0xef, 0x57, 0x8a, 0x8f, 0x3f, 0x83, 0x41, 0x85, 0x45, 0x56,
-	0xa1, 0x79, 0x42, 0x8b, 0xa8, 0xaa, 0x4f, 0xe5, 0xd9, 0x53, 0x3f, 0xca, 0x74, 0x82, 0xf5, 0x5d,
-	0xbd, 0xf8, 0x79, 0xe3, 0x13, 0xcb, 0x79, 0x09, 0xf6, 0x7e, 0x16, 0x45, 0xe5, 0xc6, 0x90, 0xf1,
-	0x62, 0x63, 0xc8, 0xf8, 0xdc, 0xcb, 0x8d, 0x2b, 0xbd, 0xfc, 0x77, 0x0b, 0xd6, 0xf6, 0x4e, 0x69,
-	0x2c, 0x0f, 0x12, 0xc9, 0x8e, 0x59, 0xe0, 0x4b, 0x96, 0xc4, 0xe4, 0x29, 0xd8, 0x49, 0x14, 0x7a,
-	0x57, 0x86, 0xa9, 0x9b, 0x44, 0xb9, 0xd5, 0x4f, 0xc1, 0x8e, 0xe9, 0x99, 0x77, 0xe5, 0x71, 0xdd,
-	0x98, 0x9e, 0x69, 0xe9, 0x87, 0x30, 0x08, 0x69, 0x44, 0x25, 0xf5, 0xca, 0xe8, 0xa8, 0xd0, 0xf5,
-	0x35, 0x71, 0x57, 0x87, 0xe3, 0x23, 0xb8, 0xa5, 0x54, 0xa6, 0x3e, 0xa7, 0xb1, 0xf4, 0x52, 0x5f,
-	0x4e, 0x31, 0x26, 0xb6, 0x3b, 0x88, 0xe9, 0xd9, 0x5b, 0xa4, 0xbe, 0xf5, 0xe5, 0xd4, 0xf9, 0x5b,
-	0x03, 0xec, 0x32, 0x98, 0xe4, 0x03, 0xe8, 0xa8, 0x63, 0x3d, 0x16, 0xe6, 0x9e, 0x68, 0xab, 0xe5,
-	0xab, 0x50, 0x55, 0x45, 0x72, 0x7c, 0x2c, 0xa8, 0x44, 0xf3, 0x9a, 0x6e, 0xbe, 0x52, 0x99, 0x25,
-	0xd8, 0x37, 0xba, 0x10, 0x56, 0x5c, 0xfc, 0x56, 0x1e, 0x9f, 0x49, 0x36, 0xa3, 0x78, 0x60, 0xd3,
-	0xd5, 0x0b, 0x32, 0x84, 0x16, 0xf5, 0xa4, 0x3f, 0xc1, 0x0c, 0xb7, 0xdd, 0x15, 0xfa, 0xce, 0x9f,
-	0x90, 0x1f, 0xc1, 0x4d, 0x91, 0x64, 0x3c, 0xa0, 0x5e, 0x71, 0x6c, 0x1b, 0xb9, 0x7d, 0x4d, 0xdd,
-	0xd7, 0x87, 0x3b, 0xd0, 0x3c, 0x66, 0xe1, 0xa8, 0x83, 0x8e, 0x59, 0xad, 0x26, 0xe1, 0xab, 0xd0,
-	0x55, 0x4c, 0xf2, 0x53, 0x80, 0x52, 0x53, 0x38, 0xea, 0x5e, 0x22, 0x6a, 0x17, 0x7a, 0x43, 0xb2,
-	0x09, 0x10, 0xb0, 0x74, 0x4a, 0xb9, 0xa7, 0x12, 0xc6, 0xc6, 0xe4, 0xb0, 0x35, 0xe5, 0x0b, 0x7a,
-	0xa1, 0xd8, 0x4c, 0x78, 0x93, 0x6f, 0x58, 0x9a, 0xd2, 0x70, 0x04, 0xe8, 0x61, 0x9b, 0x89, 0xcf,
-	0x35, 0xc1, 0xf9, 0x0a, 0xda, 0xb9, 0x71, 0xf7, 0xc0, 0x3e, 0x4d, 0xa2, 0x6c, 0x56, 0x3a, 0x6d,
-	0xe0, 0x76, 0x35, 0xe1, 0x55, 0x48, 0xee, 0x02, 0xa2, 0x24, 0x1e, 0xd1, 0x40, 0x17, 0xa1, 0x7f,
-	0xd5, 0x01, 0x77, 0xa0, 0x1d, 0x24, 0xc9, 0x09, 0xd3, 0xbe, 0xeb, 0xb8, 0xf9, 0xca, 0xf9, 0x5f,
-	0x03, 0x6e, 0x56, 0x8b, 0x45, 0x1d, 0x81, 0x5a, 0xd0, 0xd3, 0x16, 0xaa, 0x41, 0xb5, 0x87, 0x15,
-	0x6f, 0x37, 0x4c, 0x6f, 0x17, 0x5b, 0x66, 0x49, 0xa8, 0x0f, 0x18, 0xe8, 0x2d, 0x6f, 0x92, 0x90,
-	0xaa, 0x5c, 0xcf, 0x58, 0x88, 0xe1, 0x19, 0xb8, 0xea, 0x53, 0x51, 0x26, 0x2c, 0xcc, 0xc1, 0x47,
-	0x7d, 0xa2, 0x79, 0x1c, 0xf5, 0xb6, 0x75, 0xc0, 0xf5, 0x4a, 0x05, 0x7c, 0xa6, 0xa8, 0x1d, 0x1d,
-	0x45, 0xf5, 0x4d, 0xb6, 0xa0, 0xc7, 0x69, 0x1a, 0xe5, 0xb9, 0x8f, 0xce, 0xb7, 0x5d, 0x93, 0x44,
-	0xee, 0x03, 0x04, 0x49, 0x14, 0xd1, 0x00, 0x05, 0x6c, 0x14, 0x30, 0x28, 0x2a, 0xef, 0xa4, 0x8c,
-	0x3c, 0x41, 0x03, 0x74, 0x75, 0xcb, 0x6d, 0x4b, 0x19, 0x1d, 0xd2, 0x40, 0xdd, 0x23, 0x13, 0x94,
-	0x7b, 0x08, 0x5f, 0x3d, 0xdc, 0xd7, 0x55, 0x04, 0x04, 0xd9, 0x4d, 0x80, 0x09, 0x4f, 0xb2, 0x54,
-	0x73, 0xfb, 0x5b, 0x4d, 0x85, 0xe4, 0x48, 0x41, 0xf6, 0x23, 0xb8, 0x29, 0x2e, 0x66, 0x11, 0x8b,
-	0x4f, 0x3c, 0xe9, 0xf3, 0x09, 0x95, 0xa3, 0x81, 0xae, 0x80, 0x9c, 0xfa, 0x0e, 0x89, 0x4e, 0x0a,
-	0x64, 0x97, 0x53, 0x5f, 0xd2, 0xef, 0xd0, 0xb4, 0xbe, 0x1d, 0x36, 0x90, 0xdb, 0xd0, 0x4e, 0x3c,
-	0x7a, 0x1e, 0x44, 0x79, 0x89, 0xb6, 0x92, 0xbd, 0xf3, 0x20, 0x72, 0x9e, 0xc0, 0xb0, 0x72, 0x62,
-	0x0e, 0xeb, 0xeb, 0xd0, 0xa2, 0x9c, 0x27, 0x05, 0x08, 0xe9, 0x85, 0xf3, 0x3b, 0x20, 0xef, 0xd3,
-	0xf0, 0x87, 0x30, 0xcf, 0xb9, 0x0d, 0xc3, 0x8a, 0x6a, 0x6d, 0x87, 0xf3, 0x2f, 0x0b, 0xc8, 0x4b,
-	0xc4, 0x92, 0xef, 0xd7, 0xc6, 0x55, 0x75, 0xab, 0x16, 0xa3, 0xb1, 0x2a, 0xf4, 0xa5, 0x9f, 0x37,
-	0xc0, 0x3e, 0x13, 0x5a, 0xff, 0x4b, 0x5f, 0xfa, 0x79, 0x23, 0xe2, 0x34, 0xc8, 0xb8, 0xea, 0x89,
-	0x98, 0x84, 0xd8, 0x88, 0xdc, 0x82, 0x44, 0x3e, 0x86, 0x3b, 0x6c, 0x12, 0x27, 0x9c, 0xce, 0xc5,
-	0x3c, 0xed, 0xaa, 0x36, 0x0a, 0xaf, 0x6b, 0x6e, 0xb9, 0x61, 0x0f, 0x3d, 0xf7, 0x04, 0x86, 0x95,
-	0x6b, 0x5c, 0xe9, 0xe6, 0x3f, 0x5b, 0x30, 0x7a, 0x2e, 0x93, 0x19, 0x0b, 0x5c, 0xaa, 0x8c, 0xaf,
-	0x5c, 0xfd, 0x21, 0x0c, 0x14, 0x9a, 0x2f, 0x5e, 0xbf, 0x9f, 0x44, 0xe1, 0xbc, 0x5b, 0xde, 0x05,
-	0x05, 0xe8, 0x9e, 0xe1, 0x85, 0x4e, 0x12, 0x85, 0x98, 0x89, 0x0f, 0x41, 0xa1, 0xae, 0xb1, 0x5f,
-	0xcf, 0x0d, 0xfd, 0x98, 0x9e, 0x55, 0xf6, 0x2b, 0x21, 0xdc, 0xaf, 0xa1, 0xba, 0x13, 0xd3, 0x33,
-	0xb5, 0xdf, 0xb9, 0x07, 0x77, 0x97, 0xd8, 0x96, 0x87, 0xeb, 0xdf, 0x16, 0x0c, 0x9f, 0x0b, 0xc1,
-	0x26, 0xf1, 0x6f, 0x11, 0x76, 0x0a, 0xa3, 0xd7, 0xa1, 0x15, 0x24, 0x59, 0x2c, 0xd1, 0xd8, 0x96,
-	0xab, 0x17, 0x0b, 0x95, 0xd8, 0xa8, 0x55, 0xe2, 0x42, 0x2d, 0x37, 0xeb, 0xb5, 0x6c, 0xd4, 0xea,
-	0x4a, 0xa5, 0x56, 0x1f, 0x40, 0x4f, 0x05, 0xd9, 0x0b, 0x68, 0x2c, 0x29, 0xcf, 0x71, 0x1e, 0x14,
-	0x69, 0x17, 0x29, 0x4a, 0xc0, 0xec, 0x47, 0x1a, 0xea, 0x21, 0x9d, 0x37, 0xa3, 0xff, 0x58, 0xb0,
-	0x5e, 0xbd, 0x4a, 0x1e, 0xb3, 0x4b, 0xfb, 0x92, 0x82, 0x32, 0x1e, 0xe5, 0xf7, 0x50, 0x9f, 0x0a,
-	0x14, 0xd2, 0xec, 0x28, 0x62, 0x81, 0xa7, 0x18, 0xda, 0x7e, 0x5b, 0x53, 0xde, 0xf3, 0x68, 0xee,
-	0x95, 0x15, 0xd3, 0x2b, 0x04, 0x56, 0xfc, 0x4c, 0x4e, 0x8b, 0xde, 0xa4, 0xbe, 0x17, 0x3c, 0xd5,
-	0xbe, 0xce, 0x53, 0x9d, 0xba, 0xa7, 0xca, 0x4c, 0xeb, 0x9a, 0x99, 0xf6, 0x31, 0x0c, 0xf5, 0x70,
-	0x5b, 0x0d, 0xd7, 0x26, 0x40, 0xd9, 0x47, 0xc4, 0xc8, 0xd2, 0x60, 0x56, 0x34, 0x12, 0xe1, 0xfc,
-	0x12, 0xec, 0xd7, 0x89, 0xd6, 0x2b, 0xc8, 0x33, 0xb0, 0xa3, 0x62, 0x81, 0xa2, 0xbd, 0x1d, 0x32,
-	0xaf, 0xf1, 0x42, 0xce, 0x9d, 0x0b, 0x39, 0x9f, 0x41, 0xb7, 0x20, 0x17, 0x3e, 0xb3, 0x2e, 0xf3,
-	0x59, 0x63, 0xc1, 0x67, 0xce, 0x3f, 0x2d, 0x58, 0xaf, 0x9a, 0x9c, 0x87, 0xe5, 0x3d, 0x0c, 0xca,
-	0x23, 0xbc, 0x99, 0x9f, 0xe6, 0xb6, 0x3c, 0x33, 0x6d, 0xa9, 0x6f, 0x2b, 0x0d, 0x14, 0x6f, 0xfc,
-	0x54, 0xe7, 0x72, 0x3f, 0x32, 0x48, 0xe3, 0x77, 0xb0, 0x56, 0x13, 0x59, 0x32, 0xd9, 0xfd, 0xc4,
-	0x9c, 0xec, 0x2a, 0xd3, 0x69, 0xb9, 0xdb, 0x1c, 0xf7, 0x3e, 0x85, 0x0f, 0x34, 0x1c, 0xec, 0x96,
-	0x31, 0x2c, 0x7c, 0x5f, 0x0d, 0xb5, 0xb5, 0x18, 0x6a, 0x67, 0x0c, 0xa3, 0xfa, 0xd6, 0xbc, 0xfc,
-	0x26, 0xb0, 0x76, 0x28, 0x7d, 0xc9, 0x84, 0x64, 0x41, 0xf9, 0xc4, 0x58, 0xc8, 0x0d, 0xeb, 0xba,
-	0x8e, 0x58, 0xaf, 0xc3, 0x55, 0x68, 0x4a, 0x59, 0xe4, 0xaf, 0xfa, 0x54, 0x51, 0x20, 0xe6, 0x49,
-	0x79, 0x0c, 0x7e, 0x80, 0xa3, 0x54, 0x3e, 0xc8, 0x44, 0xfa, 0x91, 0x9e, 0x38, 0x56, 0x70, 0xe2,
-	0xb0, 0x91, 0x82, 0x23, 0x87, 0x6e, 0xca, 0xa1, 0xe6, 0xb6, 0xf4, 0x3c, 0xa2, 0x08, 0xc8, 0xdc,
-	0x04, 0xc0, 0x52, 0xd5, 0x55, 0xd6, 0xd6, 0x7b, 0x15, 0x65, 0x57, 0x11, 0x9c, 0xfb, 0xb0, 0xf1,
-	0x39, 0x95, 0x6a, 0x76, 0xe2, 0xbb, 0x49, 0x7c, 0xcc, 0x26, 0x19, 0xf7, 0x8d, 0x50, 0x38, 0xff,
-	0xb5, 0x60, 0xf3, 0x12, 0x81, 0xfc, 0xc2, 0x23, 0xe8, 0xcc, 0x7c, 0x21, 0x29, 0x2f, 0xaa, 0xa4,
-	0x58, 0x2e, 0xba, 0xa2, 0x71, 0x9d, 0x2b, 0x9a, 0x35, 0x57, 0xdc, 0x86, 0xf6, 0xcc, 0x3f, 0xf7,
-	0x66, 0x47, 0xf9, 0x70, 0xd4, 0x9a, 0xf9, 0xe7, 0x6f, 0x8e, 0x10, 0xd9, 0x18, 0xf7, 0x8e, 0xb2,
-	0xe0, 0x84, 0x4a, 0x51, 0x22, 0x1b, 0xe3, 0x2f, 0x34, 0x45, 0x5d, 0x5a, 0x09, 0x7c, 0x9d, 0xd1,
-	0x8c, 0x8a, 0x1c, 0x2b, 0x54, 0x73, 0xfc, 0x0d, 0x12, 0x70, 0x98, 0xc2, 0xc9, 0x12, 0x51, 0xa2,
-	0xeb, 0xe6, 0x2b, 0x27, 0x83, 0x3b, 0xea, 0x7d, 0x47, 0xe3, 0xfd, 0x84, 0xe3, 0x1b, 0xa2, 0x4c,
-	0xa0, 0x07, 0xd0, 0x0b, 0x22, 0xa6, 0xa0, 0xd2, 0x78, 0xb8, 0x81, 0x26, 0x61, 0x4b, 0x41, 0x2c,
-	0x95, 0x53, 0xaf, 0xf2, 0x56, 0x05, 0x45, 0x7a, 0xab, 0xdf, 0xab, 0x77, 0xa1, 0x2b, 0x58, 0x1c,
-	0x50, 0x2f, 0xd6, 0x0f, 0x84, 0xa6, 0xdb, 0xc1, 0xf5, 0x81, 0x70, 0xfe, 0x64, 0xc1, 0x6d, 0x7c,
-	0xf9, 0xd4, 0x9e, 0x2d, 0x57, 0xf7, 0xf8, 0x5f, 0x03, 0xa1, 0xa7, 0x68, 0x93, 0xb1, 0x27, 0xaf,
-	0xbe, 0x7b, 0xc6, 0x8c, 0xb1, 0xa8, 0xd6, 0x5d, 0xa3, 0x8b, 0x24, 0xc7, 0x57, 0x80, 0x34, 0xd1,
-	0xa5, 0x3d, 0x84, 0x96, 0x14, 0x1e, 0x42, 0x99, 0xb2, 0x73, 0x45, 0x8a, 0x03, 0x41, 0x9e, 0x02,
-	0x49, 0x7d, 0x2e, 0x99, 0x92, 0x56, 0xf3, 0xb3, 0x37, 0xf5, 0xc5, 0x14, 0x0f, 0x6b, 0xb9, 0xab,
-	0x25, 0xe7, 0x0b, 0x7a, 0xf1, 0x2b, 0x5f, 0x4c, 0x15, 0x80, 0xe3, 0x80, 0xd1, 0xc4, 0x39, 0x1e,
-	0xbf, 0x77, 0xfe, 0xda, 0x85, 0xfe, 0x21, 0xf5, 0xcf, 0x28, 0x0d, 0x31, 0x9d, 0xc8, 0xa4, 0x80,
-	0xb1, 0xea, 0x6f, 0x05, 0xf2, 0x68, 0x11, 0xaf, 0x96, 0xfe, 0xc7, 0x18, 0x7f, 0x74, 0x9d, 0x58,
-	0x8e, 0x08, 0x37, 0xc8, 0x01, 0xf4, 0x8c, 0x77, 0x3b, 0xd9, 0x30, 0x36, 0xd6, 0x7e, 0x47, 0x8c,
-	0x37, 0x2f, 0xe1, 0x16, 0xda, 0x9e, 0x59, 0xe4, 0x35, 0xf4, 0x8c, 0x81, 0xd1, 0xd4, 0x57, 0x9f,
-	0x5c, 0x4d, 0x7d, 0x4b, 0xa6, 0x4c, 0xe7, 0x86, 0xd2, 0x66, 0x8c, 0x7d, 0xa6, 0xb6, 0xfa, 0xa0,
-	0x69, 0x6a, 0x5b, 0x36, 0x2b, 0xa2, 0x36, 0x63, 0xca, 0x32, 0xb5, 0xd5, 0x67, 0x48, 0x53, 0xdb,
-	0x92, 0xd1, 0xcc, 0xb9, 0x41, 0xfe, 0x00, 0x6b, 0xb5, 0x49, 0x87, 0x38, 0xf3, 0x5d, 0x97, 0x8d,
-	0x68, 0xe3, 0x87, 0x57, 0xca, 0x94, 0xfa, 0xbf, 0x84, 0xbe, 0x39, 0x60, 0x10, 0xc3, 0xa0, 0x25,
-	0x33, 0xd4, 0xf8, 0xfe, 0x65, 0x6c, 0x53, 0xa1, 0xd9, 0xe3, 0x4c, 0x85, 0x4b, 0xba, 0xbc, 0xa9,
-	0x70, 0x59, 0x6b, 0x74, 0x6e, 0x90, 0xdf, 0xc3, 0xea, 0x62, 0xaf, 0x21, 0x1f, 0x2e, 0xba, 0xad,
-	0xd6, 0xc2, 0xc6, 0xce, 0x55, 0x22, 0xa5, 0xf2, 0x57, 0x00, 0xf3, 0x16, 0x42, 0x8c, 0x9a, 0xad,
-	0xb5, 0xb0, 0xf1, 0xc6, 0x72, 0x66, 0xa9, 0xea, 0x8f, 0x70, 0x7b, 0x29, 0x4e, 0x13, 0xa3, 0x4c,
-	0xae, 0x42, 0xfa, 0xf1, 0x8f, 0xaf, 0x95, 0x2b, 0xcf, 0xfa, 0x0a, 0x6e, 0x2d, 0xe0, 0x24, 0xd9,
-	0xaa, 0x56, 0x4d, 0x1d, 0x42, 0xc7, 0x0f, 0xcc, 0x9f, 0x4f, 0x4b, 0xc0, 0x4e, 0x55, 0xd6, 0x8b,
-	0xfb, 0xb0, 0x2a, 0x34, 0x44, 0x1c, 0x8b, 0x6d, 0x0d, 0xaf, 0x2f, 0x00, 0x6d, 0x79, 0xcb, 0x13,
-	0x99, 0x1c, 0xb5, 0xf1, 0x5f, 0xe7, 0xcf, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x32, 0x75,
-	0x14, 0xfa, 0x14, 0x00, 0x00,
+	0xd7, 0xa9, 0x0b, 0x1b, 0xaa, 0xa1, 0xa6, 0x40, 0xd2, 0xb4, 0x0f, 0xb6, 0x2c, 0xa5, 0x6e, 0x6c,
+	0xc5, 0xa5, 0xec, 0x22, 0x45, 0x81, 0x12, 0x14, 0xb9, 0xba, 0xdb, 0x8a, 0x47, 0x32, 0xbb, 0x4b,
+	0xfd, 0xc9, 0x53, 0xfb, 0x35, 0x0a, 0xf4, 0xa1, 0xdf, 0xa1, 0x8f, 0x45, 0x5f, 0x8a, 0x02, 0xfd,
+	0x1c, 0x7d, 0xec, 0xa7, 0x28, 0x76, 0x96, 0xe4, 0x2d, 0x8f, 0x27, 0x29, 0x41, 0x91, 0xb7, 0xdd,
+	0x99, 0xd9, 0xd9, 0xd9, 0xf9, 0xf3, 0x9b, 0x21, 0xa1, 0x77, 0xcc, 0x22, 0xca, 0xb7, 0x53, 0x9e,
+	0xc8, 0x84, 0x74, 0x71, 0xe3, 0xa5, 0x47, 0xce, 0x97, 0x70, 0xef, 0x75, 0x92, 0x9c, 0x64, 0xe9,
+	0x4b, 0xc6, 0x69, 0x20, 0x13, 0x7e, 0xb1, 0x17, 0x4b, 0x7e, 0xe1, 0xd2, 0xaf, 0x33, 0x2a, 0x24,
+	0xd9, 0x00, 0x3b, 0x2c, 0x18, 0x23, 0x6b, 0xcb, 0x7a, 0x6c, 0xbb, 0x73, 0x02, 0x21, 0xb0, 0x12,
+	0xfb, 0x33, 0x3a, 0x6a, 0x20, 0x03, 0xd7, 0xce, 0x1e, 0x6c, 0x2c, 0x57, 0x28, 0xd2, 0x24, 0x16,
+	0x94, 0x3c, 0x82, 0x16, 0x55, 0x04, 0xd4, 0xd6, 0xdb, 0xb9, 0xb5, 0x5d, 0x98, 0xb2, 0xad, 0xe5,
+	0x34, 0xd7, 0xf9, 0x87, 0x05, 0xe4, 0x35, 0x13, 0x52, 0x11, 0x19, 0x15, 0xdf, 0xce, 0x9e, 0x3b,
+	0xd0, 0x4e, 0x39, 0x3d, 0x66, 0xe7, 0xb9, 0x45, 0xf9, 0x8e, 0x3c, 0x85, 0x35, 0x21, 0x7d, 0x2e,
+	0xf7, 0x79, 0x32, 0xdb, 0x67, 0x11, 0x3d, 0x50, 0x46, 0x37, 0x51, 0xa4, 0xce, 0x20, 0xdb, 0x40,
+	0x58, 0x1c, 0x44, 0x99, 0x60, 0xa7, 0xf4, 0xb0, 0xe0, 0x8e, 0x56, 0xb6, 0xac, 0xc7, 0x5d, 0x77,
+	0x09, 0x87, 0xac, 0x43, 0x2b, 0x62, 0x33, 0x26, 0x47, 0xad, 0x2d, 0xeb, 0xf1, 0xc0, 0xd5, 0x1b,
+	0xe7, 0xe7, 0x30, 0xac, 0xd8, 0xff, 0xdd, 0x9e, 0xff, 0x97, 0x06, 0xb4, 0x90, 0x50, 0xfa, 0xd8,
+	0x9a, 0xfb, 0x98, 0x7c, 0x08, 0x7d, 0x26, 0xbc, 0xb9, 0x23, 0x1a, 0x68, 0x5b, 0x8f, 0x89, 0xd2,
+	0xe7, 0xe4, 0x09, 0xb4, 0x83, 0x69, 0x16, 0x9f, 0x88, 0x51, 0x73, 0xab, 0xf9, 0xb8, 0xb7, 0x33,
+	0x9c, 0x5f, 0xa4, 0x1e, 0xba, 0xab, 0x78, 0x6e, 0x2e, 0x42, 0x3e, 0x01, 0xf0, 0xa5, 0xe4, 0xec,
+	0x28, 0x93, 0x54, 0xe0, 0x4b, 0x7b, 0x3b, 0x23, 0xe3, 0x40, 0x26, 0xe8, 0xf3, 0x92, 0xef, 0x1a,
+	0xb2, 0xe4, 0x53, 0xe8, 0xd2, 0x73, 0x49, 0xe3, 0x90, 0x86, 0xa3, 0x16, 0x5e, 0xb4, 0xb9, 0xf0,
+	0xa2, 0xed, 0xbd, 0x9c, 0xaf, 0xdf, 0x57, 0x8a, 0x8f, 0x3f, 0x83, 0x41, 0x85, 0x45, 0x56, 0xa1,
+	0x79, 0x42, 0x8b, 0xa8, 0xaa, 0xa5, 0xf2, 0xec, 0xa9, 0x1f, 0x65, 0x3a, 0xc1, 0xfa, 0xae, 0xde,
+	0xfc, 0xac, 0xf1, 0x89, 0xe5, 0xbc, 0x04, 0x7b, 0x3f, 0x8b, 0xa2, 0xf2, 0x60, 0xc8, 0x78, 0x71,
+	0x30, 0x64, 0x7c, 0xee, 0xe5, 0xc6, 0x95, 0x5e, 0xfe, 0xbb, 0x05, 0x6b, 0x7b, 0xa7, 0x34, 0x96,
+	0x07, 0x89, 0x64, 0xc7, 0x2c, 0xf0, 0x25, 0x4b, 0x62, 0xf2, 0x14, 0xec, 0x24, 0x0a, 0xbd, 0x2b,
+	0xc3, 0xd4, 0x4d, 0xa2, 0xdc, 0xea, 0xa7, 0x60, 0xc7, 0xf4, 0xcc, 0xbb, 0xf2, 0xba, 0x6e, 0x4c,
+	0xcf, 0xb4, 0xf4, 0x43, 0x18, 0x84, 0x34, 0xa2, 0x92, 0x7a, 0x65, 0x74, 0x54, 0xe8, 0xfa, 0x9a,
+	0xb8, 0xab, 0xc3, 0xf1, 0x11, 0xdc, 0x52, 0x2a, 0x53, 0x9f, 0xd3, 0x58, 0x7a, 0xa9, 0x2f, 0xa7,
+	0x18, 0x13, 0xdb, 0x1d, 0xc4, 0xf4, 0xec, 0x2d, 0x52, 0xdf, 0xfa, 0x72, 0xea, 0xfc, 0xad, 0x01,
+	0x76, 0x19, 0x4c, 0xf2, 0x01, 0x74, 0xd4, 0xb5, 0x1e, 0x0b, 0x73, 0x4f, 0xb4, 0xd5, 0xf6, 0x55,
+	0xa8, 0xaa, 0x22, 0x39, 0x3e, 0x16, 0x54, 0xa2, 0x79, 0x4d, 0x37, 0xdf, 0xa9, 0xcc, 0x12, 0xec,
+	0x1b, 0x5d, 0x08, 0x2b, 0x2e, 0xae, 0x95, 0xc7, 0x67, 0x92, 0xcd, 0x28, 0x5e, 0xd8, 0x74, 0xf5,
+	0x86, 0x0c, 0xa1, 0x45, 0x3d, 0xe9, 0x4f, 0x30, 0xc3, 0x6d, 0x77, 0x85, 0xbe, 0xf3, 0x27, 0xe4,
+	0x07, 0x70, 0x53, 0x24, 0x19, 0x0f, 0xa8, 0x57, 0x5c, 0xdb, 0x46, 0x6e, 0x5f, 0x53, 0xf7, 0xf5,
+	0xe5, 0x0e, 0x34, 0x8f, 0x59, 0x38, 0xea, 0xa0, 0x63, 0x56, 0xab, 0x49, 0xf8, 0x2a, 0x74, 0x15,
+	0x93, 0xfc, 0x18, 0xa0, 0xd4, 0x14, 0x8e, 0xba, 0x97, 0x88, 0xda, 0x85, 0xde, 0x90, 0x6c, 0x02,
+	0x04, 0x2c, 0x9d, 0x52, 0xee, 0xa9, 0x84, 0xb1, 0x31, 0x39, 0x6c, 0x4d, 0xf9, 0x82, 0x5e, 0x28,
+	0x36, 0x13, 0xde, 0xe4, 0x1b, 0x96, 0xa6, 0x34, 0x1c, 0x01, 0x7a, 0xd8, 0x66, 0xe2, 0x73, 0x4d,
+	0x70, 0xbe, 0x82, 0x76, 0x6e, 0xdc, 0x3d, 0xb0, 0x4f, 0x93, 0x28, 0x9b, 0x95, 0x4e, 0x1b, 0xb8,
+	0x5d, 0x4d, 0x78, 0x15, 0x92, 0xbb, 0x80, 0x28, 0x89, 0x57, 0x34, 0xd0, 0x45, 0xe8, 0x5f, 0x75,
+	0xc1, 0x1d, 0x68, 0x07, 0x49, 0x72, 0xc2, 0xb4, 0xef, 0x3a, 0x6e, 0xbe, 0x73, 0xfe, 0xd8, 0x84,
+	0x9b, 0xd5, 0x62, 0x51, 0x57, 0xa0, 0x16, 0xf4, 0xb4, 0x85, 0x6a, 0x50, 0xed, 0x61, 0xc5, 0xdb,
+	0x0d, 0xd3, 0xdb, 0xc5, 0x91, 0x59, 0x12, 0xea, 0x0b, 0x06, 0xfa, 0xc8, 0x9b, 0x24, 0xa4, 0x2a,
+	0xd7, 0x33, 0x16, 0x62, 0x78, 0x06, 0xae, 0x5a, 0x2a, 0xca, 0x84, 0x85, 0x39, 0xf8, 0xa8, 0x25,
+	0x9a, 0xc7, 0x51, 0x6f, 0x5b, 0x07, 0x5c, 0xef, 0x54, 0xc0, 0x67, 0x8a, 0xda, 0xd1, 0x51, 0x54,
+	0x6b, 0xb2, 0x05, 0x3d, 0x4e, 0xd3, 0x28, 0xcf, 0x7d, 0x74, 0xbe, 0xed, 0x9a, 0x24, 0x72, 0x1f,
+	0x20, 0x48, 0xa2, 0x88, 0x06, 0x28, 0x60, 0xa3, 0x80, 0x41, 0x51, 0x79, 0x27, 0x65, 0xe4, 0x09,
+	0x1a, 0xa0, 0xab, 0x5b, 0x6e, 0x5b, 0xca, 0xe8, 0x90, 0x06, 0xea, 0x1d, 0x99, 0xa0, 0xdc, 0x43,
+	0xf8, 0xea, 0xe1, 0xb9, 0xae, 0x22, 0x20, 0xc8, 0x6e, 0x02, 0x4c, 0x78, 0x92, 0xa5, 0x9a, 0xdb,
+	0xdf, 0x6a, 0x2a, 0x24, 0x47, 0x0a, 0xb2, 0x1f, 0xc1, 0x4d, 0x71, 0x31, 0x8b, 0x58, 0x7c, 0xe2,
+	0x49, 0x9f, 0x4f, 0xa8, 0x1c, 0x0d, 0x74, 0x05, 0xe4, 0xd4, 0x77, 0x48, 0x54, 0x6f, 0x9f, 0x85,
+	0x3f, 0x1d, 0xdd, 0xc4, 0x0c, 0x50, 0x4b, 0x27, 0x05, 0xb2, 0xcb, 0xa9, 0x2f, 0xe9, 0x77, 0x68,
+	0x63, 0xdf, 0x0e, 0x2d, 0xc8, 0x6d, 0x68, 0x27, 0x1e, 0x3d, 0x0f, 0xa2, 0xbc, 0x68, 0x5b, 0xc9,
+	0xde, 0x79, 0x10, 0x39, 0x4f, 0x60, 0x58, 0xb9, 0x31, 0x07, 0xfa, 0x75, 0x68, 0x51, 0xce, 0x93,
+	0x02, 0x96, 0xf4, 0xc6, 0xf9, 0x2d, 0x90, 0xf7, 0x69, 0xf8, 0x7d, 0x98, 0xe7, 0xdc, 0x86, 0x61,
+	0x45, 0xb5, 0xb6, 0xc3, 0xf9, 0x97, 0x05, 0xe4, 0x25, 0xa2, 0xcb, 0xff, 0xd7, 0xd8, 0x55, 0xbd,
+	0xab, 0xa6, 0xa3, 0xd1, 0x2b, 0xf4, 0xa5, 0x9f, 0xb7, 0xc4, 0x3e, 0x13, 0x5a, 0xff, 0x4b, 0x5f,
+	0xfa, 0x79, 0x6b, 0xe2, 0x34, 0xc8, 0xb8, 0xea, 0x92, 0x98, 0x96, 0xd8, 0x9a, 0xdc, 0x82, 0x44,
+	0x3e, 0x86, 0x3b, 0x6c, 0x12, 0x27, 0x9c, 0xce, 0xc5, 0x3c, 0xed, 0xaa, 0x36, 0x0a, 0xaf, 0x6b,
+	0x6e, 0x79, 0x60, 0x0f, 0x3d, 0xf7, 0x04, 0x86, 0x95, 0x67, 0x5c, 0xe9, 0xe6, 0x3f, 0x5b, 0x30,
+	0x7a, 0x2e, 0x93, 0x19, 0x0b, 0x5c, 0xaa, 0x8c, 0xaf, 0x3c, 0xfd, 0x21, 0x0c, 0x14, 0xbe, 0x2f,
+	0x3e, 0xbf, 0x9f, 0x44, 0xe1, 0xbc, 0x7f, 0xde, 0x05, 0x05, 0xf1, 0x9e, 0xe1, 0x85, 0x4e, 0x12,
+	0x85, 0x98, 0x9b, 0x0f, 0x41, 0xe1, 0xb0, 0x71, 0x5e, 0x4f, 0x12, 0xfd, 0x98, 0x9e, 0x55, 0xce,
+	0x2b, 0x21, 0x3c, 0xaf, 0xc1, 0xbb, 0x13, 0xd3, 0x33, 0x75, 0xde, 0xb9, 0x07, 0x77, 0x97, 0xd8,
+	0x96, 0x87, 0xeb, 0xdf, 0x16, 0x0c, 0x9f, 0x0b, 0xc1, 0x26, 0xf1, 0x6f, 0x10, 0x88, 0x0a, 0xa3,
+	0xd7, 0xa1, 0x15, 0x24, 0x59, 0x2c, 0xd1, 0xd8, 0x96, 0xab, 0x37, 0x0b, 0xb5, 0xd9, 0xa8, 0xd5,
+	0xe6, 0x42, 0x75, 0x37, 0xeb, 0xd5, 0x6d, 0x54, 0xef, 0x4a, 0xa5, 0x7a, 0x1f, 0x40, 0x4f, 0x05,
+	0xd9, 0x0b, 0x68, 0x2c, 0x29, 0xcf, 0x91, 0x1f, 0x14, 0x69, 0x17, 0x29, 0x4a, 0xc0, 0xec, 0x50,
+	0x1a, 0xfc, 0x21, 0x9d, 0xb7, 0xa7, 0xff, 0x58, 0xb0, 0x5e, 0x7d, 0x4a, 0x1e, 0xb3, 0x4b, 0x3b,
+	0x95, 0x02, 0x37, 0x1e, 0xe5, 0xef, 0x50, 0x4b, 0x05, 0x13, 0x69, 0x76, 0x14, 0xb1, 0xc0, 0x53,
+	0x0c, 0x6d, 0xbf, 0xad, 0x29, 0xef, 0x79, 0x34, 0xf7, 0xca, 0x8a, 0xe9, 0x15, 0x02, 0x2b, 0x7e,
+	0x26, 0xa7, 0x45, 0xb7, 0x52, 0xeb, 0x05, 0x4f, 0xb5, 0xaf, 0xf3, 0x54, 0xa7, 0xee, 0xa9, 0x32,
+	0xd3, 0xba, 0x66, 0xa6, 0x7d, 0x0c, 0x43, 0x3d, 0xee, 0x56, 0xc3, 0xb5, 0x09, 0x50, 0x76, 0x16,
+	0x31, 0xb2, 0x34, 0xbc, 0x15, 0xad, 0x45, 0x38, 0xbf, 0x00, 0xfb, 0x75, 0xa2, 0xf5, 0x0a, 0xf2,
+	0x0c, 0xec, 0xa8, 0xd8, 0xa0, 0x68, 0x6f, 0x87, 0xcc, 0x6b, 0xbc, 0x90, 0x73, 0xe7, 0x42, 0xce,
+	0x67, 0xd0, 0x2d, 0xc8, 0x85, 0xcf, 0xac, 0xcb, 0x7c, 0xd6, 0x58, 0xf0, 0x99, 0xf3, 0x4f, 0x0b,
+	0xd6, 0xab, 0x26, 0xe7, 0x61, 0x79, 0x0f, 0x83, 0xf2, 0x0a, 0x6f, 0xe6, 0xa7, 0xb9, 0x2d, 0xcf,
+	0x4c, 0x5b, 0xea, 0xc7, 0x4a, 0x03, 0xc5, 0x1b, 0x3f, 0xd5, 0xb9, 0xdc, 0x8f, 0x0c, 0xd2, 0xf8,
+	0x1d, 0xac, 0xd5, 0x44, 0x96, 0xcc, 0x7a, 0x3f, 0x32, 0x67, 0xbd, 0xca, 0xbc, 0x5a, 0x9e, 0x36,
+	0x07, 0xc0, 0x4f, 0xe1, 0x03, 0x0d, 0x07, 0xbb, 0x65, 0x0c, 0x0b, 0xdf, 0x57, 0x43, 0x6d, 0x2d,
+	0x86, 0xda, 0x19, 0xc3, 0xa8, 0x7e, 0x34, 0x2f, 0xbf, 0x09, 0xac, 0x1d, 0x4a, 0x5f, 0x32, 0x21,
+	0x59, 0x50, 0x7e, 0x74, 0x2c, 0xe4, 0x86, 0x75, 0x5d, 0x8f, 0xac, 0xd7, 0xe1, 0x2a, 0x34, 0xa5,
+	0x2c, 0xf2, 0x57, 0x2d, 0x55, 0x14, 0x88, 0x79, 0x53, 0x1e, 0x83, 0xef, 0xe1, 0x2a, 0x95, 0x0f,
+	0x32, 0x91, 0x7e, 0xa4, 0x67, 0x90, 0x15, 0x9c, 0x41, 0x6c, 0xa4, 0xe0, 0x10, 0xa2, 0xdb, 0x74,
+	0xa8, 0xb9, 0x2d, 0x3d, 0xa1, 0x28, 0x02, 0x32, 0x37, 0x01, 0xb0, 0x54, 0x75, 0x95, 0xb5, 0xf5,
+	0x59, 0x45, 0xd9, 0x55, 0x04, 0xe7, 0x3e, 0x6c, 0x7c, 0x4e, 0xa5, 0x9a, 0xa6, 0xf8, 0x6e, 0x12,
+	0x1f, 0xb3, 0x49, 0xc6, 0x7d, 0x23, 0x14, 0xce, 0x7f, 0x2d, 0xd8, 0xbc, 0x44, 0x20, 0x7f, 0xf0,
+	0x08, 0x3a, 0x33, 0x5f, 0x48, 0xca, 0x8b, 0x2a, 0x29, 0xb6, 0x8b, 0xae, 0x68, 0x5c, 0xe7, 0x8a,
+	0x66, 0xcd, 0x15, 0xb7, 0xa1, 0x3d, 0xf3, 0xcf, 0xbd, 0xd9, 0x51, 0x3e, 0x2e, 0xb5, 0x66, 0xfe,
+	0xf9, 0x9b, 0x23, 0x44, 0x36, 0xc6, 0xbd, 0xa3, 0x2c, 0x38, 0xa1, 0x52, 0x94, 0xc8, 0xc6, 0xf8,
+	0x0b, 0x4d, 0x51, 0x8f, 0x56, 0x02, 0x5f, 0x67, 0x34, 0xa3, 0x22, 0xc7, 0x0a, 0xd5, 0x1c, 0x7f,
+	0x8d, 0x04, 0x1c, 0xaf, 0x70, 0xd6, 0x44, 0x94, 0xe8, 0xba, 0xf9, 0xce, 0xc9, 0xe0, 0x8e, 0xfa,
+	0xe2, 0xa3, 0xf1, 0x7e, 0xc2, 0xf1, 0xab, 0xa2, 0x4c, 0xa0, 0x07, 0xd0, 0x0b, 0x22, 0xa6, 0xa0,
+	0xd2, 0xf8, 0x94, 0x03, 0x4d, 0xc2, 0x96, 0x82, 0x58, 0x2a, 0xa7, 0x5e, 0xe5, 0xeb, 0x15, 0x14,
+	0xe9, 0xad, 0xfe, 0x82, 0xbd, 0x0b, 0x5d, 0xc1, 0xe2, 0x80, 0x7a, 0xb1, 0xfe, 0x64, 0x68, 0xba,
+	0x1d, 0xdc, 0x1f, 0x08, 0xe7, 0x4f, 0x16, 0xdc, 0xc6, 0x6f, 0xa1, 0xda, 0x87, 0xcc, 0xd5, 0x3d,
+	0xfe, 0x57, 0x40, 0xe8, 0x29, 0xda, 0x64, 0x9c, 0xc9, 0xab, 0xef, 0x9e, 0x31, 0x63, 0x2c, 0xaa,
+	0x75, 0xd7, 0xe8, 0x22, 0xc9, 0xf1, 0x15, 0x20, 0x4d, 0x74, 0x69, 0x0f, 0xa1, 0x25, 0x85, 0x87,
+	0x50, 0xa6, 0xec, 0x5c, 0x91, 0xe2, 0x40, 0x90, 0xa7, 0x40, 0x52, 0x9f, 0x4b, 0xa6, 0xa4, 0xd5,
+	0x44, 0xed, 0x4d, 0x7d, 0x31, 0xc5, 0xcb, 0x5a, 0xee, 0x6a, 0xc9, 0xf9, 0x82, 0x5e, 0xfc, 0xd2,
+	0x17, 0x53, 0x05, 0xe0, 0x38, 0x60, 0x34, 0x71, 0xae, 0xc3, 0xf5, 0xce, 0x5f, 0xbb, 0xd0, 0x3f,
+	0xa4, 0xfe, 0x19, 0xa5, 0x21, 0xa6, 0x13, 0x99, 0x14, 0x30, 0x56, 0xfd, 0xd1, 0x40, 0x1e, 0x2d,
+	0xe2, 0xd5, 0xd2, 0x3f, 0x1b, 0xe3, 0x8f, 0xae, 0x13, 0xcb, 0x11, 0xe1, 0x06, 0x39, 0x80, 0x9e,
+	0xf1, 0x25, 0x4f, 0x36, 0x8c, 0x83, 0xb5, 0x1f, 0x14, 0xe3, 0xcd, 0x4b, 0xb8, 0x85, 0xb6, 0x67,
+	0x16, 0x79, 0x0d, 0x3d, 0x63, 0x60, 0x34, 0xf5, 0xd5, 0x27, 0x57, 0x53, 0xdf, 0x92, 0x29, 0xd3,
+	0xb9, 0xa1, 0xb4, 0x19, 0x63, 0x9f, 0xa9, 0xad, 0x3e, 0x68, 0x9a, 0xda, 0x96, 0xcd, 0x8a, 0xa8,
+	0xcd, 0x98, 0xb2, 0x4c, 0x6d, 0xf5, 0x19, 0xd2, 0xd4, 0xb6, 0x64, 0x34, 0x73, 0x6e, 0x90, 0xdf,
+	0xc3, 0x5a, 0x6d, 0xd2, 0x21, 0xce, 0xfc, 0xd4, 0x65, 0x23, 0xda, 0xf8, 0xe1, 0x95, 0x32, 0xa5,
+	0xfe, 0x2f, 0xa1, 0x6f, 0x0e, 0x18, 0xc4, 0x30, 0x68, 0xc9, 0x0c, 0x35, 0xbe, 0x7f, 0x19, 0xdb,
+	0x54, 0x68, 0xf6, 0x38, 0x53, 0xe1, 0x92, 0x2e, 0x6f, 0x2a, 0x5c, 0xd6, 0x1a, 0x9d, 0x1b, 0xe4,
+	0x77, 0xb0, 0xba, 0xd8, 0x6b, 0xc8, 0x87, 0x8b, 0x6e, 0xab, 0xb5, 0xb0, 0xb1, 0x73, 0x95, 0x48,
+	0xa9, 0xfc, 0x15, 0xc0, 0xbc, 0x85, 0x10, 0xa3, 0x66, 0x6b, 0x2d, 0x6c, 0xbc, 0xb1, 0x9c, 0x59,
+	0xaa, 0xfa, 0x03, 0xdc, 0x5e, 0x8a, 0xd3, 0xc4, 0x28, 0x93, 0xab, 0x90, 0x7e, 0xfc, 0xc3, 0x6b,
+	0xe5, 0xca, 0xbb, 0xbe, 0x82, 0x5b, 0x0b, 0x38, 0x49, 0xb6, 0xaa, 0x55, 0x53, 0x87, 0xd0, 0xf1,
+	0x03, 0xf3, 0x77, 0xd4, 0x12, 0xb0, 0x53, 0x95, 0xf5, 0xe2, 0x3e, 0xac, 0x0a, 0x0d, 0x11, 0xc7,
+	0x62, 0x5b, 0xc3, 0xeb, 0x0b, 0x40, 0x5b, 0xde, 0xf2, 0x44, 0x26, 0x47, 0x6d, 0xfc, 0xfb, 0xf9,
+	0x93, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x24, 0x99, 0xf0, 0xba, 0x0c, 0x15, 0x00, 0x00,
 }
diff --git a/weed/replication/sink/filersink/filer_sink.go b/weed/replication/sink/filersink/filer_sink.go
index 5f055f9d1..fa9cc0f05 100644
--- a/weed/replication/sink/filersink/filer_sink.go
+++ b/weed/replication/sink/filersink/filer_sink.go
@@ -90,7 +90,7 @@ func (fs *FilerSink) CreateEntry(key string, entry *filer_pb.Entry) error {
 		}
 		glog.V(1).Infof("lookup: %v", lookupRequest)
 		if resp, err := filer_pb.LookupEntry(client, lookupRequest); err == nil {
-			if filer2.ETag(resp.Entry.Chunks) == filer2.ETag(entry.Chunks) {
+			if filer2.ETag(resp.Entry) == filer2.ETag(entry) {
 				glog.V(0).Infof("already replicated %s", key)
 				return nil
 			}
@@ -160,7 +160,7 @@ func (fs *FilerSink) UpdateEntry(key string, oldEntry *filer_pb.Entry, newParent
 		// skip if already changed
 		// this usually happens when the messages are not ordered
 		glog.V(0).Infof("late updates %s", key)
-	} else if filer2.ETag(newEntry.Chunks) == filer2.ETag(existingEntry.Chunks) {
+	} else if filer2.ETag(newEntry) == filer2.ETag(existingEntry) {
 		// skip if no change
 		// this usually happens when retrying the replication
 		glog.V(0).Infof("already replicated %s", key)
diff --git a/weed/s3api/filer_multipart.go b/weed/s3api/filer_multipart.go
index e81461dd2..85ed99854 100644
--- a/weed/s3api/filer_multipart.go
+++ b/weed/s3api/filer_multipart.go
@@ -107,7 +107,7 @@ func (s3a *S3ApiServer) completeMultipartUpload(input *s3.CompleteMultipartUploa
 		CompleteMultipartUploadOutput: s3.CompleteMultipartUploadOutput{
 			Location: aws.String(fmt.Sprintf("http://%s%s/%s", s3a.option.Filer, dirName, entryName)),
 			Bucket:   input.Bucket,
-			ETag:     aws.String("\"" + filer2.ETag(finalParts) + "\""),
+			ETag:     aws.String("\"" + filer2.ETagChunks(finalParts) + "\""),
 			Key:      objectKey(input.Key),
 		},
 	}
@@ -208,7 +208,7 @@ func (s3a *S3ApiServer) listObjectParts(input *s3.ListPartsInput) (output *ListP
 				PartNumber:   aws.Int64(int64(partNumber)),
 				LastModified: aws.Time(time.Unix(entry.Attributes.Mtime, 0)),
 				Size:         aws.Int64(int64(filer2.TotalSize(entry.Chunks))),
-				ETag:         aws.String("\"" + filer2.ETag(entry.Chunks) + "\""),
+				ETag:         aws.String("\"" + filer2.ETag(entry) + "\""),
 			})
 		}
 	}
diff --git a/weed/s3api/s3api_objects_list_handlers.go b/weed/s3api/s3api_objects_list_handlers.go
index 4469c03a6..086b9acd3 100644
--- a/weed/s3api/s3api_objects_list_handlers.go
+++ b/weed/s3api/s3api_objects_list_handlers.go
@@ -139,7 +139,7 @@ func (s3a *S3ApiServer) listFilerEntries(bucket, originalPrefix string, maxKeys
 				contents = append(contents, ListEntry{
 					Key:          fmt.Sprintf("%s%s", dir, entry.Name),
 					LastModified: time.Unix(entry.Attributes.Mtime, 0),
-					ETag:         "\"" + filer2.ETag(entry.Chunks) + "\"",
+					ETag:         "\"" + filer2.ETag(entry) + "\"",
 					Size:         int64(filer2.TotalSize(entry.Chunks)),
 					Owner: CanonicalUser{
 						ID:          fmt.Sprintf("%x", entry.Attributes.Uid),
diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go
index b59780632..96fc38bf5 100644
--- a/weed/server/filer_server_handlers_read.go
+++ b/weed/server/filer_server_handlers_read.go
@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 	"strconv"
 	"strings"
+	"time"
 
 	"github.com/chrislusf/seaweedfs/weed/filer2"
 	"github.com/chrislusf/seaweedfs/weed/glog"
@@ -78,8 +79,26 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request,
 		w.Header().Set("Content-Type", mimeType)
 	}
 
+	// if modified since
+	if !entry.Attr.Mtime.IsZero() {
+		w.Header().Set("Last-Modified", entry.Attr.Mtime.UTC().Format(http.TimeFormat))
+		if r.Header.Get("If-Modified-Since") != "" {
+			if t, parseError := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); parseError == nil {
+				if t.After(entry.Attr.Mtime) {
+					w.WriteHeader(http.StatusNotModified)
+					return
+				}
+			}
+		}
+	}
+
 	// set etag
-	setEtag(w, filer2.ETag(entry.Chunks))
+	etag := filer2.ETagEntry(entry)
+	if inm := r.Header.Get("If-None-Match"); inm == "\""+etag+"\"" {
+		w.WriteHeader(http.StatusNotModified)
+		return
+	}
+	setEtag(w, etag)
 
 	if r.Method == "HEAD" {
 		w.Header().Set("Content-Length", strconv.FormatInt(int64(filer2.TotalSize(entry.Chunks)), 10))
diff --git a/weed/server/filer_server_handlers_write.go b/weed/server/filer_server_handlers_write.go
index c3cb48ce7..33e6ee043 100644
--- a/weed/server/filer_server_handlers_write.go
+++ b/weed/server/filer_server_handlers_write.go
@@ -122,12 +122,12 @@ func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request) {
 	glog.V(4).Infof("write %s to %v", r.URL.Path, urlLocation)
 
 	u, _ := url.Parse(urlLocation)
-	ret, err := fs.uploadToVolumeServer(r, u, auth, w, fileId)
+	ret, md5value, err := fs.uploadToVolumeServer(r, u, auth, w, fileId)
 	if err != nil {
 		return
 	}
 
-	if err = fs.updateFilerStore(ctx, r, w, replication, collection, ret, fileId, ttlSeconds); err != nil {
+	if err = fs.updateFilerStore(ctx, r, w, replication, collection, ret, md5value, fileId, ttlSeconds); err != nil {
 		return
 	}
 
@@ -144,8 +144,8 @@ func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request) {
 }
 
 // update metadata in filer store
-func (fs *FilerServer) updateFilerStore(ctx context.Context, r *http.Request, w http.ResponseWriter,
-	replication string, collection string, ret *operation.UploadResult, fileId string, ttlSeconds int32) (err error) {
+func (fs *FilerServer) updateFilerStore(ctx context.Context, r *http.Request, w http.ResponseWriter, replication string,
+	collection string, ret *operation.UploadResult, md5value []byte, fileId string, ttlSeconds int32) (err error) {
 
 	stats.FilerRequestCounter.WithLabelValues("postStoreWrite").Inc()
 	start := time.Now()
@@ -186,6 +186,7 @@ func (fs *FilerServer) updateFilerStore(ctx context.Context, r *http.Request, w
 			Collection:  collection,
 			TtlSec:      ttlSeconds,
 			Mime:        ret.Mime,
+			Md5:         md5value,
 		},
 		Chunks: []*filer_pb.FileChunk{{
 			FileId: fileId,
@@ -212,15 +213,15 @@ func (fs *FilerServer) updateFilerStore(ctx context.Context, r *http.Request, w
 }
 
 // send request to volume server
-func (fs *FilerServer) uploadToVolumeServer(r *http.Request, u *url.URL, auth security.EncodedJwt, w http.ResponseWriter, fileId string) (ret *operation.UploadResult, err error) {
+func (fs *FilerServer) uploadToVolumeServer(r *http.Request, u *url.URL, auth security.EncodedJwt, w http.ResponseWriter, fileId string) (ret *operation.UploadResult, md5value []byte, err error) {
 
 	stats.FilerRequestCounter.WithLabelValues("postUpload").Inc()
 	start := time.Now()
 	defer func() { stats.FilerRequestHistogram.WithLabelValues("postUpload").Observe(time.Since(start).Seconds()) }()
 
 	ret = &operation.UploadResult{}
-	hash := md5.New()
-	var body = ioutil.NopCloser(io.TeeReader(r.Body, hash))
+	md5Hash := md5.New()
+	var body = ioutil.NopCloser(io.TeeReader(r.Body, md5Hash))
 
 	request := &http.Request{
 		Method:        r.Method,
@@ -285,7 +286,8 @@ func (fs *FilerServer) uploadToVolumeServer(r *http.Request, u *url.URL, auth se
 		}
 	}
 	// use filer calculated md5 ETag, instead of the volume server crc ETag
-	ret.ETag = fmt.Sprintf("%x", hash.Sum(nil))
+	md5value = md5Hash.Sum(nil)
+	ret.ETag = fmt.Sprintf("%x", md5value)
 	return
 }
 
diff --git a/weed/server/filer_server_handlers_write_autochunk.go b/weed/server/filer_server_handlers_write_autochunk.go
index e70826b5d..d86530bb8 100644
--- a/weed/server/filer_server_handlers_write_autochunk.go
+++ b/weed/server/filer_server_handlers_write_autochunk.go
@@ -2,7 +2,9 @@ package weed_server
 
 import (
 	"context"
+	"crypto/md5"
 	"io"
+	"io/ioutil"
 	"net/http"
 	"path"
 	"strconv"
@@ -91,10 +93,13 @@ func (fs *FilerServer) doAutoChunk(ctx context.Context, w http.ResponseWriter, r
 
 	var fileChunks []*filer_pb.FileChunk
 
+	md5Hash := md5.New()
+	var partReader = ioutil.NopCloser(io.TeeReader(part1, md5Hash))
+
 	chunkOffset := int64(0)
 
 	for chunkOffset < contentLength {
-		limitedReader := io.LimitReader(part1, int64(chunkSize))
+		limitedReader := io.LimitReader(partReader, int64(chunkSize))
 
 		// assign one file id for one chunk
 		fileId, urlLocation, auth, assignErr := fs.assignNewFileInfo(w, r, replication, collection, dataCenter, ttlString)
@@ -157,6 +162,7 @@ func (fs *FilerServer) doAutoChunk(ctx context.Context, w http.ResponseWriter, r
 			Collection:  collection,
 			TtlSec:      ttlSec,
 			Mime:        contentType,
+			Md5:         md5Hash.Sum(nil),
 		},
 		Chunks: fileChunks,
 	}