Я разрабатываю приложение для устройств Android, и одна его часть позволяет загружать и загружать документы между пользователями Google Docs и хранилищем устройства. У меня проблема в том, что я получаю различное поведение между разными версиями Android API. Я делаю большую часть разработки на уровне API 10 (Android 2.3.3). Никаких проблем на виртуальном устройстве (у меня нет реального устройства для тестирования с этим уровнем API или выше). На устройстве и эмуляторе API lvl 8 (2.2.x) и ниже я сталкиваюсь с ошибкой 411 Length required из Google Docs Api при запросе на запуск возобновляемого сеанса загрузки. Этого не происходит при запуске того же приложения на эмуляторе API lvl 10.
Я разрабатываю с помощью Eclipse и использую Google API Java Client 1.4.1-beta для связи с Docs Api. Документация, которую я использую для Google Docs API, находится здесь: http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html
Согласно указанной документации, чтобы начать возобновляемый сеанс загрузки, нужно отправить пустой запрос POST. Для «корня» Документов Google пользователя используется адрес «https://docs.google.com/feeds/upload/create-session/default/private/full". Вот как я устанавливаю заголовки для (пустого) запроса:
GoogleHeaders headers = new GoogleHeaders();
headers.contentLength = "0";
headers.gdataVersion = "3";
headers.setGoogleLogin(authToken);
//headers.contentType = getMimeType(file);
headers.setSlugFromFileName(file.getName());
headers.setApplicationName("test");
headers.set("X-Upload-Content-Length", file.length());
headers.set("X-Upload-Content-Type", getMimeType(file));
request.headers = headers;
Я также должен упомянуть, что библиотеки, которые я использую для HTTP, следующие:
com.google.api.client.googleapis.GoogleHeaders;
com.google.api.client.http.HttpRequest;
com.google.api.client.http.HttpRequestFactory;
com.google.api.client.http.HttpRequestInitializer;
com.google.api.client.http.HttpTransport;
com.google.api.client.http.HttpResponse;
com.google.api.client.http.HttpContent;
com.google.api.client.http.javanet.NetHttpTransport;
Я сделал сниффинг пакетов, чтобы увидеть заголовки, и о чудо, в разных версиях Android заголовки на самом деле не установлены одинаково. Если вы заметили, по умолчанию запрос должен быть отправлен с использованием https, поэтому я изменил его на использование http, чтобы увидеть заголовки из пакета. Вот результаты:
Использование эмулятора Android API lvl 10:
POST /feeds/upload/create-session/default/private/full?convert=false HTTP/1.1
Accept-Encoding: gzip
Authorization: **REMOVED**
Content-Length: 0
Content-Type: text/plain
GData-Version: 3
Slug: 5mbfile.txt
User-Agent: test Google-API-Java-Client/1.4.1-beta
X-Upload-Content-Length: 5242880
X-Upload-Content-Type: text/plain
Host: docs.google.com
Connection: Keep-Alive
Используя эмулятор API lvl 7:
POST /feeds/upload/create-session/default/private/full?convert=false HTTP/1.1
accept-encoding: gzip
authorization: **REMOVED**
content-type: text/plain
gdata-version: 3
slug: 5mbfile.txt
user-agent: test Google-API-Java-Client/1.4.1-beta
x-upload-content-length: 5242880
x-upload-content-type: text/plain
Host: docs.google.com
Connection: Keep-Alive
Обратите внимание на отсутствующий заголовок длины содержимого, также дело обстоит иначе. Это объясняет, почему я получаю ответ 411, но как это решить? Очевидно, моя цель — добиться одинакового поведения на всех устройствах (за исключением устройств с Android 1.x по причинам, не относящимся к этой проблеме), желательно без использования кода, зависящего от версии.
Честно говоря, я не могу придумать много подходящих решений, которые я мог бы применить в своем коде. Единственное, что мне пришло в голову:
transport = new NetHttpTransport();
transport.defaultHeaders.contentLength = Integer.toString(0);
Настройка заголовков по-разному с помощью (устаревшего) метода в API не помогает. Фактические заголовки в запросах остаются прежними.
Установка свойства с "0" или Integer.toString(0) также не имеет значения (очевидно, я немного отчаиваюсь).
Поэтому любая помощь или предложения, направленные на поиск решения, очень приветствуются. Я предоставлю больше кода, если это будет специально запрошено, а также возможно перехват пакетов для тестирования различных решений. Также существует высокая вероятность того, что это ошибка в Java-клиенте Google Api или Android. Но какой? Если решение не найдено, у меня недостаточно глубоких знаний об Android, чтобы понять, куда сообщить об этой (предполагаемой) ошибке. Поэтому, если вы подозреваете, что виновником действительно является не мой код, поделитесь своими мыслями о том, какой компонент заставляет заголовки устанавливаться по-другому.
Изменить. Algo запросил трассировку стека, вот она: http://pastebin.com/yDCCLB2P
Редактировать - я устанавливал mimetype для содержимого, хотя тело пусто. Я попытался удалить заголовок типа контента, без улучшений. Строка теперь закомментирована в приведенном выше коде.
Изменить - я пытался устранить эту проблему больше. Это трассировка стека при попытке установить заголовки двумя разными способами в одном и том же коде: http://pastebin.com/NkmFjYB3
headers.contentLength = "0";
headers.set("Content-length", "0");
При одновременном использовании они сталкиваются друг с другом. Удаление любого из них приведет к тем же результатам, что и раньше (411 Длина требуется из документов, а в запросе длина содержимого отсутствует). Коллизии не происходит (или не отображается в трассировке стека) при использовании одного из них и устаревшего способа ( transport.defaultHeaders.contentLength = "0"; ) для создания запроса. При любой комбинации любого из вышеупомянутых методов либо возникает конфликт двойного заголовка, либо запрос не имеет длины содержимого.