411 Длина требуемого ответа от Google Docs Api с использованием Android API 10 и ниже

Я разрабатываю приложение для устройств 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"; ) для создания запроса. При любой комбинации любого из вышеупомянутых методов либо возникает конфликт двойного заголовка, либо запрос не имеет длины содержимого.


person Teemu Terho    schedule 14.07.2011    source источник
comment
Должен быть какой-то способ отключить проверку запроса. Если вы перестанете выполнять валидацию запроса. он не будет проверять длину содержимого.   -  person AZ_    schedule 14.07.2011
comment
Algo, я пытался найти способ отключить проверку запроса, но не смог его найти. Проверка выполняется на стороне Google, поэтому я действительно не могу контролировать проверку. Если есть какой-то способ, о котором вы знаете, сообщите мне, так как это может включить работающее приложение. Несмотря на то, что это был бы несколько грязный способ исправить это, я был бы рад использовать его сейчас.   -  person Teemu Terho    schedule 14.07.2011
comment
Я думаю, вам следует вручную установить некоторую длину контента. Или, пожалуйста, перейдите на форумы Google и сообщите об ошибке или о чем-то еще, я уверен, что вы скоро получите ответ. и, пожалуйста, поделитесь своим решением со мной тоже.   -  person AZ_    schedule 15.07.2011
comment
Я не понимаю, что вы подразумеваете под ручной установкой длины содержимого. Самый ручной способ, который я знаю, - это то, что я делаю сейчас (жестко закодировав длину до 0). Я также попытался установить некоторый контент (одна пара ключ-значение JSON) и прочитать длину контента из этого, используя как defaultheaders, так и googleheaders. Нет успеха. Поэтому не могли бы вы уточнить, что вы имеете в виду, устанавливая длину содержимого вручную, если это не тот метод, который я использую. Чтобы сообщить об этом как об ошибке, я хотел бы услышать больше мнений, правильно ли я это делаю или нет, и следует ли сообщать об этом как об ошибке с Android или GAJC.   -  person Teemu Terho    schedule 15.07.2011
comment
Я ничего не могу сказать, не глядя на ваш код. вставьте некоторые части вашего кода.   -  person AZ_    schedule 15.07.2011
comment
Я понятия не имею, какая часть кода, кроме уже опубликованных, будет полезна для анализа этого. Если вы подозреваете дефект в некоторых из упомянутых библиотек для Http или в моем их использовании, я буду рад опубликовать эту часть. Но вставка некоторых частей кода, на мой взгляд, больше похожа на стрельбу в темноте.   -  person Teemu Terho    schedule 15.07.2011
comment
Понятия не имею о твоей проблеме, чувак, но я решил одну из своих, используя HTTP-клиент для Android. Ранее я использовал URLConnection.   -  person AZ_    schedule 19.07.2011
comment
Хорошо, приятно знать, я заметил, что некоторые другие люди также решают странные проблемы, изменяя библиотеки, которые они используют для HTTP на Android. Это, вероятно, следующий шаг и для меня, если я не могу найти исправление.   -  person Teemu Terho    schedule 19.07.2011
comment
Я рекомендую вам использовать http://developer.android.com/reference/org/apache/http/client/HttpClient.html   -  person AZ_    schedule 19.07.2011


Ответы (2)


Используйте это вместо этого:

HttpTransport transport = AndroidHttp.newCompatibleTransport();

Это то, что Google рекомендует для совместимости со всеми уровнями API, поскольку он выбирает правильную реализацию на основе версии API. Вам не нужно реализовывать это самостоятельно. Я проверял - работает как на 2.2, так и на 2.3.

person pbk    schedule 19.07.2011
comment
Я буду тестировать это завтра. На самом деле я где-то видел эту рекомендацию, но в своем безумии, пытаясь решить эту проблему, я не смог соединить точки. Я сообщу о результатах и ​​соглашусь, если это решение. - person Teemu Terho; 19.07.2011
comment
Это было правильное решение. Есть еще сложности: на Gingerbread и выше content-length надо задавать в коде, на младших версиях не задавать или есть несколько одноимённых заголовков -error. AndroidHttp.isGingerbreadOrHigher() делает это достаточно простым для исправления. Кроме того, по какой-то причине слаги не работают во всех версиях, но, надеюсь, это легко исправить. По крайней мере, первоначальная проблема теперь решена. Большое спасибо, pbk (и @ddewaele). - person Teemu Terho; 20.07.2011

Вы можете попробовать использовать ApacheHttpTransport в среде Android вместо NetHttpTransport. Есть несколько проблем с NetHttpTransport, включая ту, с которой вы столкнулись здесь, особенно до версии 2.3.

Согласно javadocs:

Начиная с SDK 2.3, настоятельно рекомендуется использовать com.google.api.client.javanet.NetHttpTransport. Их реализация HTTP-клиента Apache не так хорошо поддерживается.

Для SDK 2.2 и более ранних версий используйте com.google.api.client.apache.ApacheHttpTransport com.google.api.client.javanet.NetHttpTransport не рекомендуется из-за некоторых ошибок в реализации HttpURLConnection в Android SDK.

Я использую ApacheHttpTransport в среде Android 2.3 без каких-либо проблем. Если вы хотите поддерживать несколько уровней API И хотите следовать рекомендациям в javadocs, вам необходимо реализовать какую-то фабрику, способную предоставлять правильный транспорт на основе уровня API, на котором выполняется ваш код.

person ddewaele    schedule 19.07.2011