提交 cf2f7afb 编写于 作者: A Adam Barth

Add an implementation of url_loader.mojom using OkHttp

This CL is the beginnings of an implementation of url_loader.mojom using the
OkHttp library. OkHttp is a relatively simple HTTP client library for Android
that implements HTTP/1.1 and HTTP/2.0. We might want to use OkHttp to bootstrap
into a more full-featured network_service implementation based on //net.

R=jamesr@chromium.org

Review URL: https://codereview.chromium.org/930673002
上级 f9ddb1ca
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
android_library("oknet") {
java_files = [
"src/org/domokit/oknet/NetworkServiceImpl.java",
"src/org/domokit/oknet/UrlLoaderImpl.java",
]
deps = [
"//mojo/public/java:bindings",
"//mojo/public/java:system",
"//mojo/services/network/public/interfaces:interfaces_java",
"//third_party/okhttp",
"//third_party/okhttp:okio",
]
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.oknet;
import com.squareup.okhttp.OkHttpClient;
import org.chromium.mojo.bindings.InterfaceRequest;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.DataPipe;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojom.mojo.CookieStore;
import org.chromium.mojom.mojo.NetAddress;
import org.chromium.mojom.mojo.NetworkService;
import org.chromium.mojom.mojo.TcpBoundSocket;
import org.chromium.mojom.mojo.TcpConnectedSocket;
import org.chromium.mojom.mojo.UdpSocket;
import org.chromium.mojom.mojo.UrlLoader;
import org.chromium.mojom.mojo.WebSocket;
/**
* OkHttp implementation of NetworkService.
*/
public class NetworkServiceImpl implements NetworkService {
private OkHttpClient mClient;
private Core mCore;
public NetworkServiceImpl(Core core) {
assert core != null;
mCore = core;
mClient = new OkHttpClient();
}
@Override
public void close() {}
@Override
public void onConnectionError(MojoException e) {}
@Override
public void createUrlLoader(InterfaceRequest<UrlLoader> loader) {
UrlLoader.MANAGER.bind(new UrlLoaderImpl(mCore, mClient), loader);
}
@Override
public void getCookieStore(InterfaceRequest<CookieStore> cookieStore) {
cookieStore.close();
}
@Override
public void createWebSocket(InterfaceRequest<WebSocket> socket) {
socket.close();
}
@Override
public void createTcpBoundSocket(NetAddress localAddress,
InterfaceRequest<TcpBoundSocket> boundSocket, CreateTcpBoundSocketResponse callback) {
boundSocket.close();
}
@Override
public void createTcpConnectedSocket(NetAddress remoteAddress,
DataPipe.ConsumerHandle sendStream, DataPipe.ProducerHandle receiveStream,
InterfaceRequest<TcpConnectedSocket> clientSocket,
CreateTcpConnectedSocketResponse callback) {
sendStream.close();
receiveStream.close();
clientSocket.close();
}
@Override
public void createUdpSocket(InterfaceRequest<UdpSocket> socket) {
socket.close();
}
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.oknet;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import org.chromium.mojo.system.AsyncWaiter;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.DataPipe;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojo.system.MojoResult;
import org.chromium.mojo.system.Pair;
import org.chromium.mojom.mojo.NetworkError;
import org.chromium.mojom.mojo.UrlLoader;
import org.chromium.mojom.mojo.UrlLoaderStatus;
import org.chromium.mojom.mojo.UrlRequest;
import org.chromium.mojom.mojo.UrlResponse;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import okio.BufferedSource;
/**
* OkHttp implementation of UrlLoader.
*/
public class UrlLoaderImpl implements UrlLoader {
private Core mCore;
private OkHttpClient mClient;
private boolean mIsLoading;
private NetworkError mError;
class CopyToPipeJob {
private BufferedSource mSource;
private DataPipe.ProducerHandle mProducer;
public CopyToPipeJob(BufferedSource source, DataPipe.ProducerHandle producerHandle) {
mSource = source;
mProducer = producerHandle;
}
public void copy() throws IOException {
int result = 0;
do {
try {
ByteBuffer buffer = mProducer.beginWriteData(0, DataPipe.WriteFlags.none());
// TODO(abarth): There must be a way to do this without the temporary buffer.
byte[] tmp = new byte[buffer.capacity()];
result = mSource.read(tmp);
buffer.put(tmp);
mProducer.endWriteData(result == -1 ? 0 : result);
} catch (MojoException e) {
if (e.getMojoResult() != MojoResult.SHOULD_WAIT) throw e;
copyMoreAsync();
return;
}
} while (result != -1);
mIsLoading = false;
mProducer.close();
}
private void copyMoreAsync() {
AsyncWaiter w = mCore.getDefaultAsyncWaiter();
w.asyncWait(mProducer, Core.HandleSignals.WRITABLE, -1, new AsyncWaiter.Callback() {
@Override
public void onResult(int result) {
assert result == MojoResult.OK;
try {
copy();
} catch (IOException e) {
mIsLoading = false;
mProducer.close();
}
}
@Override
public void onError(MojoException exception) {
mIsLoading = false;
mProducer.close();
}
});
}
}
public UrlLoaderImpl(Core core, OkHttpClient client) {
assert core != null;
mCore = core;
mClient = client;
mIsLoading = false;
mError = null;
}
@Override
public void close() {}
@Override
public void onConnectionError(MojoException e) {}
@Override
public void start(UrlRequest request, StartResponse callback) {
mIsLoading = true;
mError = null;
Request.Builder builder =
new Request.Builder().url(request.url).method(request.method, null);
if (request.headers != null) {
for (String header : request.headers) {
String[] parts = header.split(":");
String name = parts[0].trim();
String value = parts.length > 1 ? parts[2].trim() : "";
builder.addHeader(name, value);
}
}
// TODO(abarth): body, responseBodyBufferSize, autoFollowRedirects, bypassCache.
final StartResponse responseCallback = callback;
Call call = mClient.newCall(builder.build());
call.enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
mError = new NetworkError();
// TODO(abarth): Which mError.code should we set?
mError.description = e.toString();
UrlResponse urlResponse = new UrlResponse();
urlResponse.error = mError;
responseCallback.call(urlResponse);
mIsLoading = false;
}
@Override
public void onResponse(Response response) {
UrlResponse urlResponse = new UrlResponse();
urlResponse.url = response.request().urlString();
urlResponse.statusCode = response.code();
urlResponse.statusLine = response.message();
Headers headers = response.headers();
urlResponse.headers = new String[headers.size()];
for (int i = 0; i < headers.size(); ++i) {
String name = headers.name(i);
String value = headers.value(i);
urlResponse.headers[i] = name + ": " + value;
}
ResponseBody body = response.body();
MediaType mediaType = body.contentType();
if (mediaType != null) {
urlResponse.mimeType = mediaType.type() + "/" + mediaType.subtype();
Charset charset = mediaType.charset();
if (charset != null)
urlResponse.charset = charset.displayName();
}
Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles =
mCore.createDataPipe(null);
DataPipe.ProducerHandle producerHandle = handles.first;
DataPipe.ConsumerHandle consumerHandle = handles.second;
urlResponse.body = consumerHandle;
responseCallback.call(urlResponse);
CopyToPipeJob job = new CopyToPipeJob(body.source(), producerHandle);
try {
job.copy();
} catch (IOException e) {
mIsLoading = false;
producerHandle.close();
}
}
});
}
@Override
public void followRedirect(FollowRedirectResponse callback) {
// TODO(abarth): Implement redirects.
callback.call(new UrlResponse());
}
@Override
public void queryStatus(QueryStatusResponse callback) {
UrlLoaderStatus status = new UrlLoaderStatus();
status.error = mError;
status.isLoading = mIsLoading;
callback.call(status);
}
}
......@@ -85,6 +85,7 @@ android_library("java") {
"//mojo/public/java:bindings",
"//mojo/public/java:system",
"//mojo/services/network/public/interfaces:interfaces_java",
"//sky/services/oknet",
]
}
......
......@@ -11,7 +11,9 @@ import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojo.system.Pair;
import org.chromium.mojo.system.impl.CoreImpl;
import org.chromium.mojom.mojo.NetworkService;
import org.chromium.mojom.mojo.ServiceProvider;
import org.domokit.oknet.NetworkServiceImpl;
/**
* A class to intialize the network.
......@@ -42,6 +44,10 @@ public class JavaServiceProvider implements ServiceProvider {
@Override
public void connectToService(String interfaceName, MessagePipeHandle pipe) {
if (interfaceName.equals(NetworkService.MANAGER.getName())) {
NetworkService.MANAGER.bind(new NetworkServiceImpl(mCore), pipe);
return;
}
pipe.close();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册