1 // Sun, 18 Jun 2000 2 // Y.Takagi 3 4 #include <list> 5 #include <algorithm> 6 #include <cstring> 7 #include <strings.h> 8 9 #define stricmp strcasecmp 10 11 #include "HttpURLConnection.h" 12 #include "Socket.h" 13 14 using namespace std; 15 16 #define DEFAULT_PORT 80; 17 18 Field::Field(char *field) 19 { 20 char *p = strtok(field, ": \t\r\n"); 21 key = p ? p : ""; 22 p = strtok(NULL, " \t\r\n"); 23 value = p ? p : ""; 24 } 25 26 Field::Field(const char *k, const char *v) 27 { 28 key = k ? k : ""; 29 value = v ? v : ""; 30 } 31 32 Field::Field(const Field &o) 33 { 34 key = o.key; 35 value = o.value; 36 } 37 38 Field &Field::operator = (const Field &o) 39 { 40 key = o.key; 41 value = o.value; 42 return *this; 43 } 44 45 bool Field::operator == (const Field &o) 46 { 47 return (key == o.key) && (value == o.value); 48 } 49 50 HttpURLConnection::HttpURLConnection(const URL &Url) 51 : connected(false), doInput(true), doOutput(false), url(Url) 52 { 53 __sock = NULL; 54 __method = "GET"; 55 __request = NULL; 56 __response = NULL; 57 __response_code = HTTP_UNKNOWN; 58 } 59 60 HttpURLConnection::~HttpURLConnection() 61 { 62 disconnect(); 63 64 if (__sock) { 65 delete __sock; 66 } 67 68 if (__request) { 69 delete __request; 70 } 71 72 if (__response) { 73 delete __response; 74 } 75 } 76 77 void HttpURLConnection::disconnect() 78 { 79 if (connected) { 80 connected = false; 81 __sock->close(); 82 } 83 } 84 85 const char *HttpURLConnection::getRequestMethod() const 86 { 87 return __method.c_str(); 88 } 89 90 void HttpURLConnection::setRequestMethod(const char *method) 91 { 92 __method = method; 93 } 94 95 void HttpURLConnection::setRequestProperty(const char *key, const char *value) 96 { 97 if (__request == NULL) { 98 __request = new Fields; 99 } 100 __request->push_back(Field(key, value)); 101 } 102 103 istream &HttpURLConnection::getInputStream() 104 { 105 if (!connected) { 106 connect(); 107 setRequest(); 108 } 109 return __sock->getInputStream(); 110 } 111 112 ostream &HttpURLConnection::getOutputStream() 113 { 114 if (!connected) { 115 connect(); 116 } 117 return __sock->getOutputStream(); 118 } 119 120 void HttpURLConnection::setDoInput(bool doInput) 121 { 122 this->doInput = doInput; 123 } 124 125 void HttpURLConnection::setDoOutput(bool doOutput) 126 { 127 this->doOutput = doOutput; 128 } 129 130 void HttpURLConnection::connect() 131 { 132 if (!connected) { 133 int port = url.getPort(); 134 if (port < 0) { 135 const char *protocol = url.getProtocol(); 136 if (!stricmp(protocol, "http")) { 137 port = DEFAULT_PORT; 138 } else if (!stricmp(protocol, "ipp")) { 139 port = 631; 140 } else { 141 port = DEFAULT_PORT; 142 } 143 } 144 __sock = new Socket(url.getHost(), port); 145 if (__sock->fail()) { 146 __error_msg = __sock->getLastError(); 147 } else { 148 connected = true; 149 } 150 } 151 } 152 153 const char *HttpURLConnection::getContentType() 154 { 155 return getHeaderField("Content-Type"); 156 } 157 158 const char *HttpURLConnection::getContentEncoding() 159 { 160 return getHeaderField("Content-Encoding"); 161 } 162 163 int HttpURLConnection::getContentLength() 164 { 165 const char *p = getHeaderField("Content-Length"); 166 return p ? atoi(p) : -1; 167 } 168 169 const char *HttpURLConnection::getHeaderField(const char *s) 170 { 171 if (__response == NULL) { 172 action(); 173 } 174 if (__response) { 175 for (Fields::iterator it = __response->begin(); it != __response->end(); it++) { 176 if ((*it).key == s) { 177 return (*it).value.c_str(); 178 } 179 } 180 } 181 return NULL; 182 } 183 184 HTTP_RESPONSECODE HttpURLConnection::getResponseCode() 185 { 186 if (__response == NULL) { 187 action(); 188 } 189 return __response_code; 190 } 191 192 const char *HttpURLConnection::getResponseMessage() 193 { 194 if (__response == NULL) { 195 action(); 196 } 197 return __response_message.c_str(); 198 } 199 200 void HttpURLConnection::action() 201 { 202 if (!connected) { 203 connect(); 204 } 205 if (connected) { 206 setRequest(); 207 } 208 if (connected) { 209 getResponse(); 210 } 211 } 212 213 void HttpURLConnection::setRequest() 214 { 215 if (connected) { 216 setRequestProperty("Host", url.getHost()); 217 ostream &os = getOutputStream(); 218 os << __method << ' ' << url.getFile() << " HTTP/1.1" << '\r' << '\n'; 219 for (Fields::iterator it = __request->begin(); it != __request->end(); it++) { 220 os << (*it).key << ": " << (*it).value << '\r' << '\n'; 221 } 222 os << '\r' << '\n'; 223 224 setContent(); 225 226 if (!doOutput) { 227 os.flush(); 228 } 229 230 if (__response) { 231 delete __response; 232 __response = NULL; 233 } 234 } 235 } 236 237 void HttpURLConnection::setContent() 238 { 239 } 240 241 void HttpURLConnection::getResponse() 242 { 243 if (connected) { 244 245 if (__response == NULL) { 246 __response = new Fields; 247 248 istream &is = getInputStream(); 249 250 char buffer[1024]; 251 252 if (!is.getline(buffer, sizeof(buffer))) { 253 __error_msg = __sock->getLastError(); 254 return; 255 } 256 buffer[is.gcount() - 2] = '\0'; 257 __response_message = buffer; 258 strtok(buffer, " "); 259 char *p = strtok(NULL, " "); 260 __response_code = p ? (HTTP_RESPONSECODE)atoi(p) : HTTP_UNKNOWN; 261 262 while (is.getline(buffer, sizeof(buffer))) { 263 if (buffer[0] == '\r') { 264 break; 265 } 266 buffer[is.gcount() - 2] = '\0'; 267 __response->push_back(Field(buffer)); 268 } 269 270 int size = getContentLength(); 271 if (size > 0) { 272 getContent(); 273 } 274 275 if (__response_code != HTTP_CONTINUE) { 276 const char *s = getHeaderField("Connection"); 277 if (s == NULL) { 278 connected = false; 279 __error_msg = "cannot found \"Connection\" field"; 280 } else if (stricmp(s, "Keep-Alive")) { 281 connected = false; 282 } 283 } 284 285 switch (__response_code) { 286 case HTTP_MOVED_TEMP: 287 { 288 const char *p = getHeaderField("Location"); 289 if (p) { 290 URL trueUrl(p); 291 url = trueUrl; 292 delete __response; 293 __response = NULL; 294 action(); 295 } 296 } 297 break; 298 case HTTP_CONTINUE: 299 delete __response; 300 __response = NULL; 301 getResponse(); 302 break; 303 default: 304 break; 305 } 306 } 307 } 308 } 309 310 void HttpURLConnection::getContent() 311 { 312 const int maxBufSize = 1024; 313 if (connected) { 314 int size = getContentLength(); 315 if (size > 0) { 316 istream &is = getInputStream(); 317 int bufsize = min(size, maxBufSize); 318 char buf[maxBufSize]; 319 while (size > 0 && is.read(buf, bufsize)) { 320 size -= bufsize; 321 } 322 } 323 } 324 } 325