blob: 4a7921f49a078f18da338127d9b3a3db634415d7 [file] [log] [blame]
Daisuke Morita8e1f8612013-11-26 15:43:21 +09001# Copyright 2013 NTT Corporation
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import re
16
17
18class ExistsAllResponseHeaders(object):
19 """
20 Specific matcher to check the existence of Swift's response headers
21
22 This matcher checks the existence of common headers for each HTTP method
23 or the target, which means account, container or object.
24 When checking the existence of 'specific' headers such as
25 X-Account-Meta-* or X-Object-Manifest for example, those headers must be
26 checked in each test code.
27 """
28
29 def __init__(self, target, method):
30 """
31 param: target Account/Container/Object
32 param: method PUT/GET/HEAD/DELETE/COPY/POST
33 """
34 self.target = target
35 self.method = method
36
37 def match(self, actual):
38 """
39 param: actual HTTP response headers
40 """
41 # Check common headers for all HTTP methods
42 if 'content-length' not in actual:
43 return NonExistentHeader('content-length')
44 if 'content-type' not in actual:
45 return NonExistentHeader('content-type')
46 if 'x-trans-id' not in actual:
47 return NonExistentHeader('x-trans-id')
48 if 'date' not in actual:
49 return NonExistentHeader('date')
50
51 # Check headers for a specific method or target
52 if self.method == 'GET' or self.method == 'HEAD':
53 if 'x-timestamp' not in actual:
54 return NonExistentHeader('x-timestamp')
55 if 'accept-ranges' not in actual:
56 return NonExistentHeader('accept-ranges')
57 if self.target == 'Account':
58 if 'x-account-bytes-used' not in actual:
59 return NonExistentHeader('x-account-bytes-used')
60 if 'x-account-container-count' not in actual:
61 return NonExistentHeader('x-account-container-count')
62 if 'x-account-object-count' not in actual:
63 return NonExistentHeader('x-account-object-count')
64 elif self.target == 'Container':
65 if 'x-container-bytes-used' not in actual:
66 return NonExistentHeader('x-container-bytes-used')
67 if 'x-container-object-count' not in actual:
68 return NonExistentHeader('x-container-object-count')
69 elif self.target == 'Object':
70 if 'etag' not in actual:
71 return NonExistentHeader('etag')
72 elif self.method == 'PUT' or self.method == 'COPY':
73 if self.target == 'Object':
74 if 'etag' not in actual:
75 return NonExistentHeader('etag')
76
77 return None
78
79
80class NonExistentHeader(object):
81 """
82 Informs an error message for end users in the case of missing a
83 certain header in Swift's responses
84 """
85
86 def __init__(self, header):
87 self.header = header
88
89 def describe(self):
90 return "%s header does not exist" % self.header
91
92 def get_details(self):
93 return {}
94
95
96class AreAllWellFormatted(object):
97 """
98 Specific matcher to check the correctness of formats of values of Swift's
99 response headers
100
101 This matcher checks the format of values of response headers.
102 When checking the format of values of 'specific' headers such as
103 X-Account-Meta-* or X-Object-Manifest for example, those values must be
104 checked in each test code.
105 """
106
107 def match(self, actual):
108 for key, value in actual.iteritems():
109 if key == 'content-length' and not value.isdigit():
110 return InvalidFormat(key, value)
111 elif key == 'x-timestamp' and not re.match("^\d+\.?\d*\Z", value):
112 return InvalidFormat(key, value)
113 elif key == 'x-account-bytes-used' and not value.isdigit():
114 return InvalidFormat(key, value)
115 elif key == 'x-account-container-count' and not value.isdigit():
116 return InvalidFormat(key, value)
117 elif key == 'x-account-object-count' and not value.isdigit():
118 return InvalidFormat(key, value)
119 elif key == 'x-container-bytes-used' and not value.isdigit():
120 return InvalidFormat(key, value)
121 elif key == 'x-container-object-count' and not value.isdigit():
122 return InvalidFormat(key, value)
123 elif key == 'content-type' and not value:
124 return InvalidFormat(key, value)
125 elif key == 'x-trans-id' and \
Christian Schwede44dcb302013-12-19 07:52:35 +0000126 not re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*", value):
Daisuke Morita8e1f8612013-11-26 15:43:21 +0900127 return InvalidFormat(key, value)
128 elif key == 'date' and not value:
129 return InvalidFormat(key, value)
130 elif key == 'accept-ranges' and not value == 'bytes':
131 return InvalidFormat(key, value)
132 elif key == 'etag' and not value.isalnum():
133 return InvalidFormat(key, value)
Daisuke Morita499bba32013-11-28 18:44:49 +0900134 elif key == 'transfer-encoding' and not value == 'chunked':
135 return InvalidFormat(key, value)
Daisuke Morita8e1f8612013-11-26 15:43:21 +0900136
137 return None
138
139
140class InvalidFormat(object):
141 """
142 Informs an error message for end users if a format of a certain header
143 is invalid
144 """
145
146 def __init__(self, key, value):
147 self.key = key
148 self.value = value
149
150 def describe(self):
151 return "InvalidFormat (%s, %s)" % (self.key, self.value)
152
153 def get_details(self):
154 return {}