|
93 | 93 | _CS_REQ_STARTED = 'Request-started' |
94 | 94 | _CS_REQ_SENT = 'Request-sent' |
95 | 95 |
|
| 96 | +class HTTPMessage(mimetools.Message): |
| 97 | + |
| 98 | + def addheader(self, key, value): |
| 99 | + """Add header for field key handling repeats.""" |
| 100 | + prev = self.dict.get(key) |
| 101 | + if prev is None: |
| 102 | + self.dict[key] = value |
| 103 | + else: |
| 104 | + combined = ", ".join((prev, value)) |
| 105 | + self.dict[key] = combined |
| 106 | + |
| 107 | + def addcontinue(self, key, more): |
| 108 | + """Add more field data from a continuation line.""" |
| 109 | + prev = self.dict[key] |
| 110 | + self.dict[key] = prev + "\n " + more |
| 111 | + |
| 112 | + def readheaders(self): |
| 113 | + """Read header lines. |
| 114 | +
|
| 115 | + Read header lines up to the entirely blank line that terminates them. |
| 116 | + The (normally blank) line that ends the headers is skipped, but not |
| 117 | + included in the returned list. If a non-header line ends the headers, |
| 118 | + (which is an error), an attempt is made to backspace over it; it is |
| 119 | + never included in the returned list. |
| 120 | +
|
| 121 | + The variable self.status is set to the empty string if all went well, |
| 122 | + otherwise it is an error message. The variable self.headers is a |
| 123 | + completely uninterpreted list of lines contained in the header (so |
| 124 | + printing them will reproduce the header exactly as it appears in the |
| 125 | + file). |
| 126 | +
|
| 127 | + If multiple header fields with the same name occur, they are combined |
| 128 | + according to the rules in RFC 2616 sec 4.2: |
| 129 | +
|
| 130 | + Appending each subsequent field-value to the first, each separated |
| 131 | + by a comma. The order in which header fields with the same field-name |
| 132 | + are received is significant to the interpretation of the combined |
| 133 | + field value. |
| 134 | + """ |
| 135 | + # XXX The implementation overrides the readheaders() method of |
| 136 | + # rfc822.Message. The base class design isn't amenable to |
| 137 | + # customized behavior here so the method here is a copy of the |
| 138 | + # base class code with a few small changes. |
| 139 | + |
| 140 | + self.dict = {} |
| 141 | + self.unixfrom = '' |
| 142 | + self.headers = list = [] |
| 143 | + self.status = '' |
| 144 | + headerseen = "" |
| 145 | + firstline = 1 |
| 146 | + startofline = unread = tell = None |
| 147 | + if hasattr(self.fp, 'unread'): |
| 148 | + unread = self.fp.unread |
| 149 | + elif self.seekable: |
| 150 | + tell = self.fp.tell |
| 151 | + while 1: |
| 152 | + if tell: |
| 153 | + try: |
| 154 | + startofline = tell() |
| 155 | + except IOError: |
| 156 | + startofline = tell = None |
| 157 | + self.seekable = 0 |
| 158 | + line = self.fp.readline() |
| 159 | + if not line: |
| 160 | + self.status = 'EOF in headers' |
| 161 | + break |
| 162 | + # Skip unix From name time lines |
| 163 | + if firstline and line.startswith('From '): |
| 164 | + self.unixfrom = self.unixfrom + line |
| 165 | + continue |
| 166 | + firstline = 0 |
| 167 | + if headerseen and line[0] in ' \t': |
| 168 | + # XXX Not sure if continuation lines are handled properly |
| 169 | + # for http and/or for repeating headers |
| 170 | + # It's a continuation line. |
| 171 | + list.append(line) |
| 172 | + x = self.dict[headerseen] + "\n " + line.strip() |
| 173 | + self.addcontinue(headerseen, line.strip()) |
| 174 | + continue |
| 175 | + elif self.iscomment(line): |
| 176 | + # It's a comment. Ignore it. |
| 177 | + continue |
| 178 | + elif self.islast(line): |
| 179 | + # Note! No pushback here! The delimiter line gets eaten. |
| 180 | + break |
| 181 | + headerseen = self.isheader(line) |
| 182 | + if headerseen: |
| 183 | + # It's a legal header line, save it. |
| 184 | + list.append(line) |
| 185 | + self.addheader(headerseen, line[len(headerseen)+1:].strip()) |
| 186 | + continue |
| 187 | + else: |
| 188 | + # It's not a header line; throw it back and stop here. |
| 189 | + if not self.dict: |
| 190 | + self.status = 'No headers' |
| 191 | + else: |
| 192 | + self.status = 'Non-header line where header expected' |
| 193 | + # Try to undo the read. |
| 194 | + if unread: |
| 195 | + unread(line) |
| 196 | + elif tell: |
| 197 | + self.fp.seek(startofline) |
| 198 | + else: |
| 199 | + self.status = self.status + '; bad seek' |
| 200 | + break |
| 201 | + |
96 | 202 |
|
97 | 203 | class HTTPResponse: |
98 | 204 |
|
@@ -186,10 +292,10 @@ def _begin(self): |
186 | 292 | if self.version == 9: |
187 | 293 | self.chunked = 0 |
188 | 294 | self.will_close = 1 |
189 | | - self.msg = mimetools.Message(StringIO()) |
| 295 | + self.msg = HTTPMessage(StringIO()) |
190 | 296 | return |
191 | 297 |
|
192 | | - self.msg = mimetools.Message(self.fp, 0) |
| 298 | + self.msg = HTTPMessage(self.fp, 0) |
193 | 299 | if self.debuglevel > 0: |
194 | 300 | for hdr in self.msg.headers: |
195 | 301 | print "header:", hdr, |
|
0 commit comments