Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3913)

Unified Diff: runtime/bin/dartutils.cc

Issue 15966002: Add support for loading scripts from http (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« runtime/bin/bin.gypi ('K') | « runtime/bin/dartutils.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/bin/dartutils.cc
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index e11392720af3d1e3b49b465ef70a4bfb11567b4b..d90241de1b6e48fbcd54585c4446a90d04c697c4 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+#include <errno.h> // NOLINT
+
#include "bin/dartutils.h"
#include "include/dart_api.h"
@@ -14,6 +16,7 @@
#include "bin/file.h"
#include "bin/io_buffer.h"
#include "bin/utils.h"
+#include "bin/socket.h"
namespace dart {
namespace bin {
@@ -27,6 +30,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 +146,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
@@ -233,19 +243,187 @@ void DartUtils::CloseFile(void* stream) {
delete reinterpret_cast<File*>(stream);
}
+// Writes string into socket. Spins until socket connects.
+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) {
+ if (errno == ENOTCONN) {
Ivan Posva 2013/05/24 17:53:43 Spinning is in general not a good idea. Also there
Cutch 2013/05/24 17:59:56 Socket::CreateConnect sets the socket to be non-bl
siva 2013/05/24 23:23:18 I agree the notion of spinning seems contrived. M
Cutch 2013/05/25 01:37:42 Done.
+ // CreateConnect sets the socket to be non-blocking, we may get here
+ // before the connection has occurred. Spin here.
+ continue;
+ }
+ return cursor;
+ }
+ cursor += r;
+ len -= r;
+ } while (len > 0);
+ return cursor;
+}
+
+static uint8_t* SocketReadUntilEOF(intptr_t socket, intptr_t* response_len) {
+ intptr_t buffer_size = 16 * 1024;
siva 2013/05/24 23:23:18 const intptr_t kbufferSize = 16 * KB; to be unifo
Cutch 2013/05/25 01:37:42 Done.
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(buffer_size));
+ intptr_t buffer_cursor = 0;
+ do {
+ if (buffer_cursor == buffer_size-1) {
+ // Double buffer size.
+ buffer_size *= 2;
+ buffer = reinterpret_cast<uint8_t*>(realloc(buffer, buffer_size));
+ }
siva 2013/05/24 23:23:18 This check for buffer overflow could be at the bot
Cutch 2013/05/25 01:37:42 Done.
+ ssize_t bytes_read = read(socket, &buffer[buffer_cursor],
+ buffer_size-buffer_cursor-1);
siva 2013/05/24 23:23:18 We normally have a space between operators in our
Cutch 2013/05/25 01:37:42 Done.
+ if (bytes_read < 0) {
+ if (errno == EWOULDBLOCK) {
+ continue;
+ }
+ free(buffer);
+ return NULL;
+ }
+
+ buffer_cursor += bytes_read;
siva 2013/05/24 23:23:18 The increment could be after the if (bytes_read ==
Cutch 2013/05/25 01:37:42 It can't be because I need to add the zero charact
+
+ if (bytes_read == 0) {
+ *response_len = buffer_cursor;
+ buffer[buffer_cursor] = '\0';
+ break;
+ }
+ } while (true);
+ return buffer;
+}
+
+static bool HttpGetRequestOkay(const char* response) {
+ static const char* kOkayReply = "HTTP/1.0 200 OK";
siva 2013/05/24 23:23:18 Are we going with HTTP 1.0 and not 1.1 There seem
Cutch 2013/05/25 01:37:42 Yes, we don't need anything from 1.1.
+ 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.
siva 2013/05/24 23:23:18 Is this comment valid for this use case? The binar
Cutch 2013/05/25 01:37:42 I believe what Ivan was saying is that the binary
+#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* domain, const char* path,
+ int port, intptr_t* response_len,
+ const char** error_msg) {
+ OSError* error = NULL;
+ SocketAddresses* addresses = Socket::LookupAddress(domain,
+ -1,
+ &error);
+ if (addresses == NULL || addresses->count() == 0) {
+ SET_ERROR_MSG(error_msg, "Unable to resolve %s", domain);
+ 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) {
+ preferred_address = i;
+ break;
+ }
+ }
+ intptr_t tcp_client = Socket::CreateConnect(
+ addresses->GetAt(preferred_address)->addr(),
+ port);
+ if (tcp_client < 0) {
+ SET_ERROR_MSG(error_msg, "Unable to connect to %s:%d", domain, 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, domain);
+ char* get_request = reinterpret_cast<char*>(malloc(len + 1));
+ snprintf(get_request, len + 1, format, path, domain);
+ 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", domain, port);
+ Socket::Close(tcp_client);
+ return NULL;
+ }
+ }
+ // Consume response.
+ Socket::SetNonBlocking(tcp_client);
+ 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", domain, port);
+ return NULL;
+ }
+ if (HttpGetRequestOkay(reinterpret_cast<const char*>(response)) == false) {
+ SET_ERROR_MSG(error_msg, "Invalid HTTP response from %s:%d", domain, port);
+ free(response);
+ return NULL;
+ }
+ return response;
+}
+
+
+Dart_Handle DartUtils::ReadStringFromHttp(const char* script_uri) {
+ Dart_Handle result;
+ Dart_Handle uri = NewString(script_uri);
+ Dart_Handle builtin_lib =
+ Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
+ Dart_Handle path = PathFromUri(uri, builtin_lib);
+ Dart_Handle domain = DomainFromUri(uri, builtin_lib);
+ Dart_Handle port = PortFromUri(uri, builtin_lib);
siva 2013/05/24 23:23:18 path, domain and port could be Error objects becau
Cutch 2013/05/25 01:37:42 Good catch. Done.
+ const char* path_str = NULL;
+ const char* domain_str = NULL;
+ int64_t port_int = 0;
+ result = Dart_StringToCString(path, &path_str);
+ if (Dart_IsError(result)) {
+ return result;
+ }
+ result = Dart_StringToCString(domain, &domain_str);
+ if (Dart_IsError(result)) {
+ return result;
+ }
+ if (GetInt64Value(port, &port_int) == false) {
+ return Dart_Error("Invalid port");
+ }
+ const char* error_msg = NULL;
+ intptr_t len;
+ const uint8_t* text_buffer = HttpGetRequest(domain_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 +477,42 @@ Dart_Handle DartUtils::FilePathFromUri(Dart_Handle script_uri,
}
+Dart_Handle DartUtils::PathFromUri(Dart_Handle script_uri,
+ Dart_Handle builtin_lib) {
+ const int kNumArgs = 1;
+ Dart_Handle dart_args[kNumArgs];
+ dart_args[0] = script_uri;
+ return Dart_Invoke(builtin_lib,
+ NewString("_pathFromHttpUri"),
+ kNumArgs,
+ dart_args);
+}
+
+
+Dart_Handle DartUtils::DomainFromUri(Dart_Handle script_uri,
+ Dart_Handle builtin_lib) {
+ const int kNumArgs = 1;
+ Dart_Handle dart_args[kNumArgs];
+ dart_args[0] = script_uri;
+ return Dart_Invoke(builtin_lib,
+ NewString("_domainFromHttpUri"),
+ kNumArgs,
+ dart_args);
+}
+
+
+Dart_Handle DartUtils::PortFromUri(Dart_Handle script_uri,
+ Dart_Handle builtin_lib) {
+ const int kNumArgs = 1;
+ Dart_Handle dart_args[kNumArgs];
+ dart_args[0] = script_uri;
+ return Dart_Invoke(builtin_lib,
+ NewString("_portFromHttpUri"),
+ kNumArgs,
+ dart_args);
+}
siva 2013/05/24 23:23:18 Seems like these three functions could have been f
Cutch 2013/05/25 01:37:42 Done.
+
+
Dart_Handle DartUtils::ResolveUri(Dart_Handle library_url,
Dart_Handle url,
Dart_Handle builtin_lib) {
@@ -426,8 +640,64 @@ void DartUtils::WriteMagicNumber(File* file) {
}
+Dart_Handle DartUtils::LoadScriptHttp(const char* script_uri,
+ Dart_Handle builtin_lib) {
+ Dart_Handle uri = NewString(script_uri);
+ Dart_Handle path = PathFromUri(uri, builtin_lib);
+ Dart_Handle domain = DomainFromUri(uri, builtin_lib);
+ Dart_Handle port = PortFromUri(uri, builtin_lib);
+ const char* path_str = NULL;
+ const char* domain_str = NULL;
+ int64_t port_int = 0;
+ Dart_Handle result;
+ result = Dart_StringToCString(path, &path_str);
+ if (Dart_IsError(result)) {
+ return result;
+ }
+ result = Dart_StringToCString(domain, &domain_str);
+ if (Dart_IsError(result)) {
+ return result;
+ }
+ if (GetInt64Value(port, &port_int) == false) {
+ return Dart_Error("Invalid port");
+ }
+ const char* error_msg = NULL;
+ intptr_t len;
+ const uint8_t* text_buffer;
+ text_buffer = HttpGetRequest(domain_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 +737,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 +747,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);
« runtime/bin/bin.gypi ('K') | « runtime/bin/dartutils.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698