Byłbym bardzo wdzięczny za pomoc w zrozumieniu tego zachowania Apache.
Komunikuję się z PHP z aplikacji iPhone Objective-C w aplikacji / json. Kompresja Gzip jest włączona na serwerze i żądana przez klienta.
Z mojego .htaccess:
AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php application/json
W przypadku małych żądań Apache ustawia nagłówek „Content-Length”. Na przykład (te wartości są wyprowadzane w celu C z nagłówka):
Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 185; <-------------
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:27 GMT";
"Keep-Alive" = "timeout=3, max=149";
Server = Apache;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 217;
X-Uncompressed-Content-Length to nagłówek, który dodaję do rozmiaru nieskompresowanego ciągu JSON.
Jak widać, to żądanie jest bardzo małe (217 bajtów).
Oto nagłówki z większego żądania (282888 bajtów):
Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:29 GMT";
"Keep-Alive" = "timeout=3, max=148";
Server = Apache;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 282888;
Zauważ, że długość treści nie jest podana.
Moje pytania:
- Dlaczego Apache nie wysyła Content-Length dla większego żądania?
- Czy fakt, że ustawiono „Contend-Encoding = gzip”, oznacza, że kompresja gzip nadal działa w przypadku większego żądania, mimo że nie mogę zweryfikować różnicy wielkości?
- Czy istnieje sposób, w jaki mogę skłonić Apache do uwzględnienia faktycznej długości treści w przypadku tych większych żądań, aby dokładniej zgłaszać wykorzystanie danych użytkownikom?
Tej aplikacji można używać w przypadku planów danych, które są drogie, dlatego pragnę zgłosić użytkownikowi rzeczywiste użycie, a nie 30-70% zawyżone zużycie (kilkaset dodatkowych KB może nie brzmieć dużo - ale te plany mogą kosztować od 1 USD i 10 USD za MB!).
Z góry dziękuję.
źródło
Wygląda na to, że Apache wykonuje kodowanie fragmentaryczne, co oznacza, że może wysyłać dane podczas gzipowania, zamiast czekać na pełną odpowiedź. Jest to dość standardowa praktyka, ale nie znam wystarczająco Apache, aby stwierdzić, czy można ją wyłączyć.
źródło
OK, udało mi się to rozwiązać. Jak słusznie zauważa Martin F, Apache dzieli odpowiedź na części, więc rozmiar zawartości nie jest znany. Dla wielu osób jest to pożądane (strona ładuje się szybciej). Kosztem tego jest brak możliwości zgłoszenia postępu pobierania.
Dla takich jak ja, którzy naprawdę chcą zgłosić postęp pobierania, jeśli użyjesz automatycznego wsparcia gzip dla Apache lub PHP, niewiele możesz zrobić. Rozwiązaniem jest zrobienie tego ręcznie. To łatwiejsze niż się wydaje:
Jeśli wysyłasz całe pliki, jest to świetny przykład w PHP wymuszania pojedynczej porcji (z Content-Length): http://www.php.net/manual/en/function.ob-start.php # 94741
Jeśli wysyłasz wygenerowane dane, użyj gzencode, aby zakodować swoje dane, jak w powyższym przykładzie. Warunkiem jest, aby wszystkie dane wyjściowe były przechowywane w zmiennej (możesz użyć ob_start, aby to zrobić, jeśli potrzebujesz buforować, a następnie uzyskać zawartość bufora).
I voila!
Kolejną wielką zaletą robienia tego samemu jest to, że możesz ustawić poziom kompresji. Jest to świetne dla mojej aplikacji mobilnej, ponieważ mogę ustawić najwyższy poziom kompresji (więc moi użytkownicy płacą mniej za dane!) - podczas gdy serwer prawdopodobnie używa tylko średniego poziomu kompresji w celu uzyskania lepszego kompromisu między procesorem a rozmiarem. Poziomy kompresji to coś, co uważam, że możesz zmienić tylko wtedy, gdy możesz edytować httpd.conf (którego na hostingu współdzielonym nie mogę).
Zachowałem więc moją DEFLATE .htaccess dla wszystkiego oprócz mojej aplikacji / odpowiedzi json, które teraz koduję w powyższy sposób.
Jeszcze raz dziękuję Martin F., dałeś mi iskrę potrzebną do rozwiązania tego :)
źródło
strlen($replyBody)
zamiastmb_strlen($replyBody, 'latin1')
. Długość treści to tylko liczba bajtów (nie znaków), co daje ci strlen (). Używanie mb_strlen () z rodzajem „latin1” działa, ponieważ znaki latin1 mają zawsze 8 bitów, ale może mieć problemy z kodowaniem, które produkuje bajty, które nie są poprawnymi znakami latin1.