Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "bin/vmstats_impl.h" | 5 #include "bin/vmstats_impl.h" |
| 6 | 6 |
| 7 #include <sstream> | 7 #include <sstream> |
| 8 | 8 |
| 9 #include "bin/file.h" | 9 #include "bin/file.h" |
| 10 #include "bin/log.h" | 10 #include "bin/log.h" |
| 11 #include "bin/platform.h" | 11 #include "bin/platform.h" |
| 12 #include "bin/resources.h" | |
| 12 #include "bin/socket.h" | 13 #include "bin/socket.h" |
| 13 #include "bin/thread.h" | 14 #include "bin/thread.h" |
| 14 #include "bin/utils.h" | 15 #include "bin/utils.h" |
| 15 #include "include/dart_debugger_api.h" | 16 #include "include/dart_debugger_api.h" |
| 16 #include "platform/json.h" | 17 #include "platform/json.h" |
| 17 | 18 |
| 18 #define BUFSIZE 8192 | 19 #define BUFSIZE 8192 |
| 19 #define RETRY_PAUSE 100 // milliseconds | 20 #define RETRY_PAUSE 100 // milliseconds |
| 20 | 21 |
| 21 static const char* INDEX_HTML = "index.html"; | 22 static const char* INDEX_HTML = "index.html"; |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 return "image/png"; | 126 return "image/png"; |
| 126 } | 127 } |
| 127 if (!strcmp(suffix, ".jpg") || !strcmp(suffix, ".jpeg")) { | 128 if (!strcmp(suffix, ".jpg") || !strcmp(suffix, ".jpeg")) { |
| 128 return "image/jpeg"; | 129 return "image/jpeg"; |
| 129 } | 130 } |
| 130 } | 131 } |
| 131 return "text/plain"; | 132 return "text/plain"; |
| 132 } | 133 } |
| 133 | 134 |
| 134 | 135 |
| 136 void writeResponse(intptr_t socket, const char* content_type, | |
| 137 const char* data, size_t length) { | |
| 138 char buffer[BUFSIZE + 1]; | |
| 139 snprintf(buffer, BUFSIZE, | |
| 140 "HTTP/1.1 200 OK\nContent-Type: %s\nContent-Length: %"Pu"\n\n", | |
| 141 content_type, length); | |
| 142 Socket::Write(socket, buffer, strlen(buffer)); | |
| 143 Socket::Write(socket, data, length); | |
| 144 Socket::Write(socket, "\n", 1); | |
| 145 } | |
| 146 | |
| 147 | |
| 148 void writeErrorResponse(intptr_t socket, int error_num, const char* error, | |
| 149 const char* description) { | |
| 150 char buffer[BUFSIZE + 1]; | |
|
siva
2013/03/06 06:32:20
Do you really need 8k of stack space to write send
Tom Ball
2013/03/06 17:47:17
Added printf function that malloc's the necessary
| |
| 151 if (description != NULL) { | |
| 152 // Create body first, so its length is known when creating the header. | |
| 153 int body_len = snprintf(buffer, BUFSIZE, | |
|
srdjan
2013/03/06 01:14:37
s/int/intptr_t/
(also below for len).
Tom Ball
2013/03/06 17:47:17
Done.
| |
| 154 "<html><head><title>%d %s</title></head>\n" | |
| 155 "<body><h1>%s</h1>\n%s\n</body></html>\n", | |
| 156 error_num, error, error, description); | |
| 157 char* body = strdup(buffer); | |
| 158 int len = snprintf(buffer, BUFSIZE, | |
| 159 "HTTP/1.1 %d %s\n" | |
| 160 "Content-Length: %d\n" | |
| 161 "Connection: close\n" | |
| 162 "Content-Type: text/html\n\n", | |
| 163 error_num, error, body_len); | |
| 164 Socket::Write(socket, buffer, len); | |
| 165 Socket::Write(socket, body, body_len); | |
| 166 free(body); | |
| 167 } else { | |
| 168 int len = snprintf(buffer, BUFSIZE, "HTTP/1.1 %d %s\n\n", | |
| 169 error_num, error); | |
| 170 Socket::Write(socket, buffer, len); | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 | |
| 135 void VmStats::WebServer(uword bind_address) { | 175 void VmStats::WebServer(uword bind_address) { |
| 136 while (true) { | 176 while (true) { |
| 137 intptr_t socket = ServerSocket::Accept(bind_address); | 177 intptr_t socket = ServerSocket::Accept(bind_address); |
| 138 if (socket == ServerSocket::kTemporaryFailure) { | 178 if (socket == ServerSocket::kTemporaryFailure) { |
| 139 // Not a real failure, woke up but no connection available. | 179 // Not a real failure, woke up but no connection available. |
| 140 | 180 |
| 141 // Use MonitorLocker.Wait(), since it has finer granularity than sleep(). | 181 // Use MonitorLocker.Wait(), since it has finer granularity than sleep(). |
| 142 dart::Monitor m; | 182 dart::Monitor m; |
| 143 MonitorLocker ml(&m); | 183 MonitorLocker ml(&m); |
| 144 ml.Wait(RETRY_PAUSE); | 184 ml.Wait(RETRY_PAUSE); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 160 if (len <= 0) { | 200 if (len <= 0) { |
| 161 // Invalid HTTP request, ignore. | 201 // Invalid HTTP request, ignore. |
| 162 continue; | 202 continue; |
| 163 } | 203 } |
| 164 buffer[len] = '\0'; | 204 buffer[len] = '\0'; |
| 165 | 205 |
| 166 // Verify it's a GET request. | 206 // Verify it's a GET request. |
| 167 // TODO(tball): support POST requests. | 207 // TODO(tball): support POST requests. |
| 168 if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) { | 208 if (strncmp("GET ", buffer, 4) != 0 && strncmp("get ", buffer, 4) != 0) { |
| 169 Log::PrintErr("Unsupported HTTP request type"); | 209 Log::PrintErr("Unsupported HTTP request type"); |
| 170 const char* response = "HTTP/1.1 403 Forbidden\n" | 210 writeErrorResponse(socket, 403, "Forbidden", |
| 171 "Content-Length: 120\n" | 211 "Unsupported HTTP request type"); |
| 172 "Connection: close\n" | |
| 173 "Content-Type: text/html\n\n" | |
| 174 "<html><head>\n<title>403 Forbidden</title>\n</head>" | |
| 175 "<body>\n<h1>Forbidden</h1>\nUnsupported HTTP request type\n</body>" | |
| 176 "</html>\n"; | |
| 177 Socket::Write(socket, response, strlen(response)); | |
| 178 Socket::Close(socket); | 212 Socket::Close(socket); |
| 179 continue; | 213 continue; |
| 180 } | 214 } |
| 181 | 215 |
| 182 // Extract GET URL, and null-terminate URL in case request line has | 216 // Extract GET URL, and null-terminate URL in case request line has |
| 183 // HTTP version. | 217 // HTTP version. |
| 184 for (int i = 4; i < len; i++) { | 218 for (int i = 4; i < len; i++) { |
| 185 if (buffer[i] == ' ') { | 219 if (buffer[i] == ' ') { |
| 186 buffer[i] = '\0'; | 220 buffer[i] = '\0'; |
| 187 } | 221 } |
| 188 } | 222 } |
| 189 char* url = &buffer[4]; | 223 char* url = strdup(&buffer[4]); |
| 190 | 224 |
| 191 Log::Print("vmstats: %s requested\n", url); | 225 Log::Print("vmstats: %s requested\n", url); |
| 192 char* content = NULL; | 226 char* content = NULL; |
| 193 | 227 |
| 194 // Check for VmStats-specific URLs. | 228 // Check for VmStats-specific URLs. |
| 195 if (strcmp(url, "/isolates") == 0) { | 229 if (strcmp(url, "/isolates") == 0) { |
| 196 content = instance_->IsolatesStatus(); | 230 content = instance_->IsolatesStatus(); |
| 197 } else { | 231 } else { |
| 198 // Check plug-ins. | 232 // Check plug-ins. |
| 199 content = VmStatusService::GetVmStatus(url); | 233 content = VmStatusService::GetVmStatus(url); |
| 200 } | 234 } |
| 201 | 235 |
| 202 if (content != NULL) { | 236 if (content != NULL) { |
| 203 size_t content_len = strlen(content); | 237 writeResponse(socket, "application/json", content, strlen(content)); |
| 204 len = snprintf(buffer, BUFSIZE, | |
| 205 "HTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\n" | |
| 206 "Content-Length: %"Pu"\n\n", | |
| 207 content_len); | |
| 208 Socket::Write(socket, buffer, strlen(buffer)); | |
| 209 Socket::Write(socket, content, content_len); | |
| 210 Socket::Write(socket, "\n", 1); | |
| 211 Socket::Write(socket, buffer, strlen(buffer)); | |
| 212 free(content); | 238 free(content); |
| 213 } else { | 239 } else { |
| 214 // No status content with this URL, return file or resource content. | 240 // No status content with this URL, return file or resource content. |
| 215 std::string path(instance_->root_directory_); | 241 std::string path(instance_->root_directory_); |
| 216 path.append(url); | 242 path.append(url); |
| 217 | 243 |
| 218 // Expand directory URLs. | 244 // Expand directory URLs. |
| 219 if (strcmp(url, "/") == 0) { | 245 if (strcmp(url, "/") == 0) { |
| 220 path.append(VMSTATS_HTML); | 246 path.append(VMSTATS_HTML); |
| 221 } else if (url[strlen(url) - 1] == '/') { | 247 } else if (url[strlen(url) - 1] == '/') { |
| 222 path.append(INDEX_HTML); | 248 path.append(INDEX_HTML); |
| 223 } | 249 } |
| 224 | 250 |
| 225 bool success = false; | 251 bool success = false; |
| 252 char* text_buffer = NULL; | |
| 253 const char* content_type = ContentType(path.c_str()); | |
| 226 if (File::Exists(path.c_str())) { | 254 if (File::Exists(path.c_str())) { |
| 227 File* f = File::Open(path.c_str(), File::kRead); | 255 File* f = File::Open(path.c_str(), File::kRead); |
| 228 if (f != NULL) { | 256 if (f != NULL) { |
| 229 intptr_t len = f->Length(); | 257 intptr_t len = f->Length(); |
| 230 char* text_buffer = reinterpret_cast<char*>(malloc(len)); | 258 text_buffer = reinterpret_cast<char*>(malloc(len)); |
| 231 if (f->ReadFully(text_buffer, len)) { | 259 if (f->ReadFully(text_buffer, len)) { |
| 232 const char* content_type = ContentType(path.c_str()); | 260 writeResponse(socket, content_type, text_buffer, len); |
| 233 snprintf(buffer, BUFSIZE, | |
| 234 "HTTP/1.1 200 OK\nContent-Type: %s\n" | |
| 235 "Content-Length: %"Pu"\n\n", | |
| 236 content_type, len); | |
| 237 Socket::Write(socket, buffer, strlen(buffer)); | |
| 238 Socket::Write(socket, text_buffer, len); | |
| 239 Socket::Write(socket, "\n", 1); | |
| 240 success = true; | 261 success = true; |
| 241 } | 262 } |
| 242 free(text_buffer); | 263 free(text_buffer); |
| 243 delete f; | 264 delete f; |
| 244 } | 265 } |
| 245 } else { | 266 } else { |
| 246 // TODO(tball): look up linked in resource. | 267 const char* resource; |
| 268 intptr_t len = Resources::ResourceLookup(path.c_str(), &resource); | |
| 269 if (len != Resources::kNoSuchInstance) { | |
| 270 ASSERT(len >= 0); | |
| 271 writeResponse(socket, content_type, resource, len); | |
| 272 success = true; | |
| 273 } | |
| 247 } | 274 } |
| 248 if (!success) { | 275 if (!success) { |
| 249 const char* response = "HTTP/1.1 404 Not Found\n\n"; | 276 snprintf(buffer, BUFSIZE, |
| 250 Socket::Write(socket, response, strlen(response)); | 277 "URL <a href=\"%s\">%s</a> not found.", url, url); |
| 278 writeErrorResponse(socket, 404, "Not Found", buffer); | |
| 251 } | 279 } |
| 252 } | 280 } |
| 253 Socket::Close(socket); | 281 Socket::Close(socket); |
| 282 free(url); | |
| 254 } | 283 } |
| 255 | 284 |
| 256 Shutdown(); | 285 Shutdown(); |
| 257 } | 286 } |
| 258 | 287 |
| 259 | 288 |
| 260 char* VmStats::IsolatesStatus() { | 289 char* VmStats::IsolatesStatus() { |
| 261 std::ostringstream stream; | 290 std::ostringstream stream; |
| 262 stream << '{' << std::endl; | 291 stream << '{' << std::endl; |
| 263 stream << "\"isolates\": [" << std::endl; | 292 stream << "\"isolates\": [" << std::endl; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 VmStatusPlugin* plugin = instance_->registered_plugin_list_; | 352 VmStatusPlugin* plugin = instance_->registered_plugin_list_; |
| 324 while (plugin != NULL) { | 353 while (plugin != NULL) { |
| 325 char* result = (plugin->callback())(request); | 354 char* result = (plugin->callback())(request); |
| 326 if (result != NULL) { | 355 if (result != NULL) { |
| 327 return result; | 356 return result; |
| 328 } | 357 } |
| 329 plugin = plugin->next(); | 358 plugin = plugin->next(); |
| 330 } | 359 } |
| 331 return NULL; | 360 return NULL; |
| 332 } | 361 } |
| OLD | NEW |