Chromium Code Reviews| Index: runtime/bin/dartutils.cc |
| diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc |
| index e11392720af3d1e3b49b465ef70a4bfb11567b4b..5619869853355d468161fb2a17ac8a170df1fbd3 100644 |
| --- a/runtime/bin/dartutils.cc |
| +++ b/runtime/bin/dartutils.cc |
| @@ -14,6 +14,7 @@ |
| #include "bin/file.h" |
| #include "bin/io_buffer.h" |
| #include "bin/utils.h" |
| +#include "bin/socket.h" |
| namespace dart { |
| namespace bin { |
| @@ -27,6 +28,7 @@ const char* DartUtils::kCoreLibURL = "dart:core"; |
| const char* DartUtils::kIOLibURL = "dart:io"; |
| const char* DartUtils::kIOLibPatchURL = "dart:io-patch"; |
| const char* DartUtils::kUriLibURL = "dart:uri"; |
| +const char* DartUtils::kHttpScheme = "http:"; |
| const char* DartUtils::kIdFieldName = "_id"; |
| @@ -142,6 +144,12 @@ bool DartUtils::IsDartSchemeURL(const char* url_name) { |
| } |
| +bool DartUtils::IsHttpSchemeURL(const char* url_name) { |
| + static const intptr_t kHttpSchemeLen = strlen(kHttpScheme); |
| + return (strncmp(url_name, kHttpScheme, kHttpSchemeLen) == 0); |
| +} |
| + |
| + |
| bool DartUtils::IsDartExtensionSchemeURL(const char* url_name) { |
| static const intptr_t kDartExtensionSchemeLen = strlen(kDartExtensionScheme); |
| // If the URL starts with "dartext:" then it is considered as a special |
| @@ -234,18 +242,220 @@ void DartUtils::CloseFile(void* stream) { |
| } |
| +// Writes string into socket. |
|
Ivan Posva
2013/05/28 18:25:32
Please document the meaning of the return value.
A
Cutch
2013/05/28 21:39:55
I return the error code and print it.
|
| +static intptr_t SocketWriteString(intptr_t socket, const char* str, |
| + intptr_t len) { |
| + int r; |
| + intptr_t cursor = 0; |
| + do { |
| + r = Socket::Write(socket, &str[cursor], len); |
| + if (r < 0) { |
| + return cursor; |
| + } |
| + cursor += r; |
| + len -= r; |
| + } while (len > 0); |
| + return cursor; |
| +} |
| + |
| + |
| +static uint8_t* SocketReadUntilEOF(intptr_t socket, intptr_t* response_len) { |
| + const intptr_t kInitialBufferSize = 16 * KB; |
| + intptr_t buffer_size = kInitialBufferSize; |
| + uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(buffer_size)); |
| + ASSERT(buffer != NULL); |
| + intptr_t buffer_cursor = 0; |
| + do { |
| + ssize_t bytes_read = Socket::Read(socket, &buffer[buffer_cursor], |
| + buffer_size - buffer_cursor - 1); |
| + if (bytes_read < 0) { |
| + free(buffer); |
| + return NULL; |
| + } |
| + |
| + buffer_cursor += bytes_read; |
| + |
| + if (bytes_read == 0) { |
| + *response_len = buffer_cursor; |
| + buffer[buffer_cursor] = '\0'; |
| + break; |
| + } |
| + |
| + // There is still more data to be read, check that we have room in the |
| + // buffer for more data. |
| + if (buffer_cursor == buffer_size - 1) { |
| + // Buffer is full. Increase buffer size. |
| + buffer_size *= 2; |
| + buffer = reinterpret_cast<uint8_t*>(realloc(buffer, buffer_size)); |
| + ASSERT(buffer != NULL); |
| + } |
| + } while (true); |
| + return buffer; |
| +} |
| + |
| + |
| +static bool HttpGetRequestOkay(const char* response) { |
| + static const char* kOkayReply = "HTTP/1.0 200 OK"; |
| + static const intptr_t kOkayReplyLen = strlen(kOkayReply); |
| + return (strncmp(response, kOkayReply, kOkayReplyLen) == 0); |
| +} |
| + |
| + |
| +static const uint8_t* HttpRequestGetPayload(const char* response) { |
| + const char* split = strstr(response, "\r\n\r\n"); |
| + if (split != NULL) { |
| + return reinterpret_cast<const uint8_t*>(split+4); |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| +// TODO(iposva): Allocate from the zone instead of leaking error string |
| +// here. On the other hand the binary is about the exit anyway. |
| +#define SET_ERROR_MSG(error_msg, format, ...) \ |
| + intptr_t len = snprintf(NULL, 0, format, __VA_ARGS__); \ |
| + char *msg = reinterpret_cast<char*>(malloc(len + 1)); \ |
| + snprintf(msg, len + 1, format, __VA_ARGS__); \ |
| + *error_msg = msg |
| + |
| + |
| +static const uint8_t* HttpGetRequest(const char* host, const char* path, |
| + int port, intptr_t* response_len, |
| + const char** error_msg) { |
| + OSError* error = NULL; |
| + SocketAddresses* addresses = Socket::LookupAddress(host, |
| + -1, |
|
Ivan Posva
2013/05/28 18:25:32
Fits on one line?
Cutch
2013/05/28 21:39:55
Done.
|
| + &error); |
| + if (addresses == NULL || addresses->count() == 0) { |
| + SET_ERROR_MSG(error_msg, "Unable to resolve %s", host); |
| + return NULL; |
| + } |
| + |
| + int preferred_address = 0; |
| + for (int i = 0; i < addresses->count(); i++) { |
| + SocketAddress* address = addresses->GetAt(i); |
| + if (address->GetType() == SocketAddress::ADDRESS_LOOPBACK_IP_V4) { |
| + // Prefer the IP_V4 loop back. |
| + preferred_address = i; |
| + break; |
| + } |
| + } |
| + |
| + RawAddr addr = addresses->GetAt(preferred_address)->addr(); |
| + intptr_t tcp_client = Socket::Create(addr); |
| + if (tcp_client < 0) { |
| + SET_ERROR_MSG(error_msg, "Unable to create socket to %s:%d", host, port); |
| + return NULL; |
| + } |
| + Socket::Connect(tcp_client, addr, port); |
| + if (tcp_client < 0) { |
| + SET_ERROR_MSG(error_msg, "Unable to connect to %s:%d", host, port); |
| + return NULL; |
| + } |
| + // Send get request. |
| + { |
| + const char* format = |
| + "GET %s HTTP/1.0\r\nUser-Agent: Dart VM\r\nHost: %s\r\n\r\n"; |
| + intptr_t len = snprintf(NULL, 0, format, path, host); |
| + char* get_request = reinterpret_cast<char*>(malloc(len + 1)); |
| + snprintf(get_request, len + 1, format, path, host); |
| + intptr_t r = SocketWriteString(tcp_client, get_request, len); |
| + free(get_request); |
| + if (r != len) { |
| + SET_ERROR_MSG(error_msg, "Unable to write to %s:%d", host, port); |
| + Socket::Close(tcp_client); |
| + return NULL; |
| + } |
| + } |
| + // Consume response. |
| + uint8_t* response = SocketReadUntilEOF(tcp_client, response_len); |
| + // Close socket. |
| + Socket::Close(tcp_client); |
| + if (response == NULL) { |
| + SET_ERROR_MSG(error_msg, "Unable to read from %s:%d", host, port); |
| + return NULL; |
| + } |
| + if (HttpGetRequestOkay(reinterpret_cast<const char*>(response)) == false) { |
| + SET_ERROR_MSG(error_msg, "Invalid HTTP response from %s:%d", host, port); |
| + free(response); |
| + return NULL; |
| + } |
| + return response; |
| +} |
| + |
| + |
| +static Dart_Handle ParseHttpUri(const char* script_uri, const char** host_str, |
| + int64_t* port_int, const char** path_str) { |
| + ASSERT(script_uri != NULL); |
| + ASSERT(host_str != NULL); |
| + ASSERT(port_int != NULL); |
| + ASSERT(path_str != NULL); |
| + Dart_Handle result; |
| + Dart_Handle uri = DartUtils::NewString(script_uri); |
| + Dart_Handle builtin_lib = |
| + Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary); |
| + Dart_Handle path = DartUtils::PathFromUri(uri, builtin_lib); |
| + if (Dart_IsError(path)) { |
| + return path; |
| + } |
| + Dart_Handle host = DartUtils::DomainFromUri(uri, builtin_lib); |
| + if (Dart_IsError(host)) { |
| + return host; |
| + } |
| + Dart_Handle port = DartUtils::PortFromUri(uri, builtin_lib); |
| + if (Dart_IsError(port)) { |
| + return port; |
| + } |
| + result = Dart_StringToCString(path, path_str); |
| + if (Dart_IsError(result)) { |
| + return result; |
| + } |
| + result = Dart_StringToCString(host, host_str); |
| + if (Dart_IsError(result)) { |
| + return result; |
| + } |
| + if (DartUtils::GetInt64Value(port, port_int) == false) { |
| + return Dart_Error("Invalid port"); |
| + } |
| + return result; |
| +} |
| + |
| + |
| +Dart_Handle DartUtils::ReadStringFromHttp(const char* script_uri) { |
| + const char* host_str = NULL; |
| + int64_t port_int = 0; |
| + const char* path_str = NULL; |
| + Dart_Handle result = ParseHttpUri(script_uri, &host_str, &port_int, |
| + &path_str); |
| + if (Dart_IsError(result)) { |
| + return result; |
| + } |
| + const char* error_msg = NULL; |
| + intptr_t len; |
| + const uint8_t* text_buffer = HttpGetRequest(host_str, path_str, port_int, |
| + &len, &error_msg); |
| + if (text_buffer == NULL) { |
| + return Dart_Error(error_msg); |
| + } |
| + const uint8_t* payload = HttpRequestGetPayload( |
| + reinterpret_cast<const char*>(text_buffer)); |
| + if (payload == NULL) { |
| + return Dart_Error("Invalid HTTP response."); |
| + } |
| + // Subtract HTTP response from length. |
| + len -= (payload-text_buffer); |
| + ASSERT(len >= 0); |
| + Dart_Handle str = Dart_NewStringFromUTF8(payload, len); |
| + return str; |
| +} |
| + |
| + |
| static const uint8_t* ReadFileFully(const char* filename, |
| intptr_t* file_len, |
| const char** error_msg) { |
| void* stream = DartUtils::OpenFile(filename, false); |
| if (stream == NULL) { |
| - const char* format = "Unable to open file: %s"; |
| - intptr_t len = snprintf(NULL, 0, format, filename); |
| - // TODO(iposva): Allocate from the zone instead of leaking error string |
| - // here. On the other hand the binary is about the exit anyway. |
| - char* msg = reinterpret_cast<char*>(malloc(len + 1)); |
| - snprintf(msg, len + 1, format, filename); |
| - *error_msg = msg; |
| + SET_ERROR_MSG(error_msg, "Unable to open file: %s", filename); |
| return NULL; |
| } |
| *file_len = -1; |
| @@ -299,6 +509,32 @@ Dart_Handle DartUtils::FilePathFromUri(Dart_Handle script_uri, |
| } |
| +static Dart_Handle SingleArgDart_Invoke(Dart_Handle arg, Dart_Handle lib, |
| + const char* method) { |
| + const int kNumArgs = 1; |
| + Dart_Handle dart_args[kNumArgs]; |
| + dart_args[0] = arg; |
| + return Dart_Invoke(lib, DartUtils::NewString(method), kNumArgs, dart_args); |
| +} |
| + |
| +Dart_Handle DartUtils::PathFromUri(Dart_Handle script_uri, |
| + Dart_Handle builtin_lib) { |
| + return SingleArgDart_Invoke(script_uri, builtin_lib, "_pathFromHttpUri"); |
| +} |
| + |
| + |
| +Dart_Handle DartUtils::DomainFromUri(Dart_Handle script_uri, |
| + Dart_Handle builtin_lib) { |
| + return SingleArgDart_Invoke(script_uri, builtin_lib, "_domainFromHttpUri"); |
| +} |
| + |
| + |
| +Dart_Handle DartUtils::PortFromUri(Dart_Handle script_uri, |
| + Dart_Handle builtin_lib) { |
| + return SingleArgDart_Invoke(script_uri, builtin_lib, "_portFromHttpUri"); |
| +} |
| + |
| + |
| Dart_Handle DartUtils::ResolveUri(Dart_Handle library_url, |
| Dart_Handle url, |
| Dart_Handle builtin_lib) { |
| @@ -426,8 +662,57 @@ void DartUtils::WriteMagicNumber(File* file) { |
| } |
| +Dart_Handle DartUtils::LoadScriptHttp(const char* script_uri, |
| + Dart_Handle builtin_lib) { |
| + Dart_Handle uri = NewString(script_uri); |
| + if (Dart_IsError(uri)) { |
| + return uri; |
| + } |
| + const char* host_str = NULL; |
| + int64_t port_int = 0; |
| + const char* path_str = NULL; |
| + Dart_Handle result = ParseHttpUri(script_uri, &host_str, &port_int, |
| + &path_str); |
| + if (Dart_IsError(result)) { |
| + return result; |
| + } |
| + const char* error_msg = NULL; |
| + intptr_t len; |
| + const uint8_t* text_buffer; |
| + text_buffer = HttpGetRequest(host_str, path_str, port_int, &len, |
| + &error_msg); |
| + if (text_buffer == NULL) { |
| + return Dart_Error(error_msg); |
| + } |
| + const uint8_t* payload = HttpRequestGetPayload( |
| + reinterpret_cast<const char*>(text_buffer)); |
| + if (payload == NULL) { |
| + return Dart_Error("Invalid HTTP response."); |
| + } |
| + // Subtract HTTP response from length. |
| + len -= (payload-text_buffer); |
| + ASSERT(len >= 0); |
| + // At this point we have received a valid HTTP 200 reply and |
| + // payload points at the beginning of the script or snapshot. |
| + bool is_snapshot = false; |
| + payload = SniffForMagicNumber(payload, &len, &is_snapshot); |
| + if (is_snapshot) { |
| + return Dart_LoadScriptFromSnapshot(payload, len); |
| + } else { |
| + Dart_Handle source = Dart_NewStringFromUTF8(payload, len); |
| + if (Dart_IsError(source)) { |
| + return source; |
| + } |
| + return Dart_LoadScript(uri, source, 0, 0); |
| + } |
| +} |
| + |
| + |
| Dart_Handle DartUtils::LoadScript(const char* script_uri, |
| Dart_Handle builtin_lib) { |
| + if (DartUtils::IsHttpSchemeURL(script_uri)) { |
| + return LoadScriptHttp(script_uri, builtin_lib); |
| + } |
| Dart_Handle resolved_script_uri; |
| resolved_script_uri = ResolveScriptUri(NewString(script_uri), builtin_lib); |
| if (Dart_IsError(resolved_script_uri)) { |
| @@ -467,6 +752,7 @@ Dart_Handle DartUtils::LoadSource(CommandLineOptions* url_mapping, |
| Dart_Handle url, |
| Dart_LibraryTag tag, |
| const char* url_string) { |
| + bool is_http_scheme_url = DartUtils::IsHttpSchemeURL(url_string); |
| if (url_mapping != NULL && IsDartSchemeURL(url_string)) { |
| const char* mapped_url_string = MapLibraryUrl(url_mapping, url_string); |
| if (mapped_url_string == NULL) { |
| @@ -476,12 +762,19 @@ Dart_Handle DartUtils::LoadSource(CommandLineOptions* url_mapping, |
| // URL mapping specifies and load it. |
| url_string = mapped_url_string; |
| } |
| - // The tag is either an import or a source tag. |
| - // Read the file and load it according to the specified tag. |
| - Dart_Handle source = DartUtils::ReadStringFromFile(url_string); |
| + Dart_Handle source; |
| + if (is_http_scheme_url) { |
| + // Read the file over http. |
| + source = DartUtils::ReadStringFromHttp(url_string); |
| + } else { |
| + // Read the file. |
| + source = DartUtils::ReadStringFromFile(url_string); |
| + } |
| if (Dart_IsError(source)) { |
| return source; // source contains the error string. |
| } |
| + // The tag is either an import or a source tag. |
| + // Load it according to the specified tag. |
| if (tag == kImportTag) { |
| // Return library object or an error string. |
| return Dart_LoadLibrary(url, source); |