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