Każdy z poniższych punktów kosztował od kilku godzin do dwóch dni i żadnego nie dało się przewidzieć z dokumentacji.
iPhone po cichu pogarsza to, co wysyła
Film nagrany w 4K trafiał na serwer jako krótki plik 480 na 360, a zdjęcia bywały mniejsze niż oryginały. Nic w aplikacji tego nie robiło. Źródła WebKita mówią jasno: przy wyborze pliku z biblioteki zdjęć Safari na iOS zawsze przekodowuje filmy, pamięta rozmiar wybrany w oknie wyboru na zupełnie innej stronie, a Live Photo oddaje jako jego krótki filmik w niskiej rozdzielczości. Strona internetowa nie ma jak tego wyłączyć. Dlatego aplikacja mówi prawdę: po wyborze pokazuje faktyczną rozdzielczość i długość, ostrzega, gdy widać ślady zmniejszenia, i podsuwa drogę przez aplikację Pliki, która oddaje oryginał. Wymiary każdego pliku zapisujemy na serwerze, więc pytanie, dlaczego film jest słaby, rozstrzyga zapis, a nie domysły.
Zawieszenie na 96 procentach: odpowiedź, która dociera, ale nigdy nie jest widziana
Wysyłki z iPhone'ów dochodziły do 96 do 99 procent i stawały. Logi serwera pokazywały, że wszystko przyjął i zapisał, z czasem odpowiedzi półtorej sekundy. Telefon czekał w nieskończoność. Dwie przyczyny, obie w warstwie sieciowej Apple: przeglądarka gubiła się, gdy ciało żądania było czytane wprost z pliku na dysku, a przy kilku wysyłkach naraz żądanie dostawało kompletną odpowiedź HTTP 200, ale nigdy nie zgłaszało, że się zakończyło. Porcje są teraz najpierw wczytywane do pamięci i wysyłane jako gotowy bufor; jeśli widać pełną, poprawną odpowiedź, porcja liczy się jako dostarczona, a zombie-żądanie jest porzucane; licznik bezczynności, 30 sekund, uruchamia ponowną próbę z rosnącym odstępem. Telefon dosyła też na koniec krótki dziennik zdarzeń, zapisywany przy metadanych pliku, więc zachowanie cudzego telefonu da się później odczytać na serwerze.
Nowoczesny build, stary telefon, biała strona
Na kilkuletnim telefonie z nieaktualizowaną przeglądarką strona wyświetlała się bez stylów, a wysyłka nie działała. Framework buduje kod pod przeglądarki z ostatnich dwóch lat, biblioteka stylów owija wszystko w nowoczesną konstrukcję CSS, którą starszy silnik pomija razem z całym arkuszem, a kilka nowych jednostek i skrótów składniowych zatrzymuje cały skrypt. Do tego dwie pułapki: minifikator CSS usuwał starszą z dwóch wartości zapasowych jako duplikat, a oficjalny mechanizm frameworka do ładowania skryptu „przed całą resztą” ładował go po własnych paczkach. Rozwiązanie: jawna lista obsługiwanych przeglądarek cofnięta o kilka lat, spłaszczenie nowoczesnej konstrukcji przy budowaniu, jednostki zapasowe, łatki na brakujące funkcje i kilka wierszy skryptu wpisanych wprost w nagłówek dokumentu.
Wielki plik kontra domyślne ustawienia serwera
Przy 5 GB na plik większość ustawień domyślnych działa przeciw nam. Serwer pośredniczący buforuje całe żądanie, zanim przekaże je dalej, zapisując gigabajty na własnym dysku tylko po to, żeby zaraz je oddać; limity rozmiaru i czasy oczekiwania są krótkie. Wysyłka idzie teraz strumieniem przez wszystkie warstwy bez pośredniego buforowania, każda porcja trafia na swoje miejsce w rzadkim pliku, zakończenie jest operacją atomową, a powtórzone zakończenie po zerwanej sieci nie tworzy duplikatu. Standardowa biblioteka formularzy w Pythonie dekodowała nazwy plików w starym kodowaniu zachodnioeuropejskim, więc polskie znaki potrafiły wywrócić całe żądanie; zastąpił ją krótki odczyt nagłówka z poprawnym kodowaniem.
Jak testować przeglądarkę sprzed sześciu lat
Nowoczesne narzędzia do testów nie potrafią już sterować przeglądarkami z tamtego okresu. Zamiast zakładać, że powinno działać, pobraliśmy archiwalne wersje silników i sterowaliśmy nimi bezpośrednio protokołem niższego poziomu, każdą pełną ścieżką: otwarcie strony, wysłanie prawdziwego pliku, porównanie sumy kontrolnej z oryginałem, otwarcie galerii i podstron.