httputils / charset

Begonnen von herrmannj, 27 März 2020, 00:23:09

Vorheriges Thema - Nächstes Thema

herrmannj

Moin,

ich habe gerade mal wieder gefühlte 10 Jahre mit charset & co verbracht (verbringe ... )

Ausgang:
- ich hole Daten per httputils, nonblocking. (zb https://corona.lmao.ninja/countries)
- Ich sage dem Server das ich utf8 (und latin) möchte: Accept-Charset: utf-8, iso-8859-1
- der Server Antwortet mit application/json] und utf-8
- ich nehme das charset aus der Antwort und hole einen decoder 'my $decoder = Encode::find_encoding($suspect);'
- der decoder sagt mir das er 'utf-8-strict' wählt. ... und vergeigt das (im Beispiel 'R�union' soll 'Réunion')

Erst, und nur, wenn ich dem decoder explizit sage dass er ' iso-8859-1' nehmen soll, passt das Ergebnis. Das passt aber eigentlich eben nicht zur Server Antwort.

Jetzt würde ich sagen dass der Server halt falsch liefert. Ist aber bei einer zweiten, ganz anderen Quelle genauso.

$data (als return vom nonblocking get) habe ich mir angeschaut. Ein 'ü' ist das wirklich als ord(252) drin, ohne prefix. Da kommt wirklich iso-8859-1 an. Gut, ich kann das Wandeln erzwingen und die Server Antwort (utf-8) ignorieren - dann passt das vom Ergebnis. Allerdings: wenn ich den link oben im Browser aufrufe, der bekommt das richtig hin.

Irgend jemand eine Idee was (vmtl ich) falsch mache?

justme1968

und wieder das zeitfresser thema :)

ich würde auch auf einen fehler im server oder dessen konfiguration tippen. könnte ja in beiden fällen die gleiche software sein.

hast du mal probiert das accept-charset weg zu lassen? der browser sendet das ja glaube ich auch nicht.

macht es einen unterschied wenn du nur utf-8 angibst?

macht es einen unterschied wenn du im accept-charset die reihenfolge mit ;q=... vorgibst und utf-8 bevorzugst?

wenn irgendetwas davon geht würde ich sagen es ist wirklich der server.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Ich vermute ein anderes Problem: mit
define lmao at +*01:00 { HttpUtils_NonblockingGet({ url=>"https://corona.lmao.ninja/countries",\
  callback=>sub($$$){ Log 1,"lmao:$_[1]" if($_[1]);;\
  json2reading($defs{lmao},$_[2],undef,undef,'hashKeyRename($ret,"^([0-9]+)_country:(R.*union)","^([0-9]+)")')} }) }

kriege ich utf8 Daten, siehe Anhang.

justme1968

so wird aber wird aber ohne accept-charset geholt. dann antwortet der server vermutlich einfach per default mit utf-8.



hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

herrmannj

Moin,

sorry, ist gestern noch spät geworden und ich hing heute die ganze Zeit in conf calls, sonst hätte ich schon meine eigene Aussage zurückgenommen.

Muss mich auch bei Andre entschuldigen dem ich vor einigen Tagen noch so entschieden zum Thema utf8/UNICODE widersprochen habe. Hast Recht gehabt Andre und ich bin glücklicherweise die letzten Jahre damit verschont gebliebe.

Rudi, ja geht bei Dir. Geht weil das jetzt alles utf8 durch geroutet wird. Problem so, wenn man mit den Daten was machen will wie length($land) / regex usw. dann ist blöd. length auf 'Réunion' als so erzeugtes Reading ist 8. length('Réunion') als code ist 7. length($reading) == length('Réunion')  ist dann false. usw.

Das man im code ein $var = 'Réunion' (was ja unicode ergibt) nehmen kann und das sogar im Browser ankommt liegt mMn an dem glücklichen Umstand das browser alle accept gzip setzen und da dort in fhemweb die Sonderlocke der Konvertierung. So etwa line #610.

Rudi: würdest Du da Vorschläge grundsätzlich in Betracht ziehen wollen? Eigentlich "müsste" die convertierung unicode -> utf8 dort standardmäßig greifen. Die Kunst wird sein das irgendwie abwärtskompatibel zu gestalten. Ich bin mir nicht sicher ob ich den Deckel überhaupt aufmachen möchte.

Am liebsten wäre es mir aber wenn ich perl dazu bewegen könnte bei 'Réunion' den zweiten Buchstaben als ein Zeichen in length, subst, regex etc zu sehen (ohne das ich das in UNICODE konvertieren muss). Ich wüsste aber nicht wie das geht. Geht das irgendwie ?

Beim JsonMod bin ich darüber 'gestolpert' weil ja die readingnamen automatisch aus dem JSON generiert werden damit ich gültige Reading Namen daraus machen kann. Da funktionieren jetzt die Ersetzungen nicht vernünftig - aus gesagten Gründen.


justme1968

alles gut :).

wie schon geschrieben: die änderung wäre schön, aber wenn man das tatsächlich angeht muss man alle stellen und ebenen auf ein mal umstellen bzw. 'richtig' machen. wenn man nur eine stellen anfasst fällt es einem dann woanders auf die füße.

die unvollständige liste wäre etwa:
- minimale perl version festelegen
- alle quelltexte mit use utf8 versehen
- intern tatsächlich als utf-8 markierte strings verwenden. (oder anderes encoding)
- überall wo daten rein kommen oder raus gehen das encoding setzen bzw. explizit konvertieren
- alle regex kontrollieren und anpassen
- allen anwendern sagen das alle regex potentiell angepasst werden müssen wenn
  umlaute ähnliches vorkommen
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

herrmannj

je eben, Die Liste (longpoll, brwoser, log) ist nicht mal der Anfang. Da macht man sich keine Freunde. Am besten fahre ich wohl wenn ich die Strings vom utf8 ins unicode hole, dann verarbeite und wieder zurück nach bevor ich die irgendwo ablege utf8. Nasty aber der Gesundheit und den Wohlbefinden am dienlichsten. ...

Einen Trick wie man regex und co beibringen kann utf8 wie unicode zu behandeln gibts nicht, oder? (oder frage ich google falsch?).

rudolfkoenig

Ich wuerde FHEM bei utf-8 lassen, ein Umbau kostet viel Aufwand, und kann fuer etliche Module nicht durchgefuehrt werden, mangels Hardware oder Support vom Maintainer. Womoeglich zieht es auch andere Probleme nach sich.

Das Problem mit length verstehe ich, wuesste aber nicht, wo man length konkret braucht.
Regexp ist was anderes, vermutlich sollten wir eine Umlaut faehige Regexp Funktion bauen fuer die, die es unbedingt brauchen.

justme1968

@rudi: das problem ist das fhem eben nicht wirklich utf-8 ist.

fhem verwendet zwar intern utf-8 byte order, aber nicht als echte perl utf-8 strings sondern in binär strings.

die sehen zwar auf den ersten blick gleich aus, es lässt sich aber so keine der in perl eigentlich vorhandenen möglichkeiten nutzen um mit den encodings umzugehen. perl könnte alles mögliche automatisch. das besagte length, regex matching, zahlen erkennung, umwandeln, ...  . aber so geht eben nichts davon und man muss an 1000 stellen von hand mit workarounds eingreifen. das problem an den workarounds ist das es alles sehr fragil macht und eine änderung an einer stelle alle möglichen probleme an anderen stellen erzeugt.

ein beispiel für length: man kann aktuell nicht mit printf und fester string länge text tabellen (ohne html! sondern auf der konsole) ausrichten weil perl die umlaute als zwei byte statt ein zeichen erkennt.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Bist du sicher, dass du mit "perl utf-8 strings" nicht perl unicode strings meinst?
Wenn sowas doch gibt, dann muesste syswrite damit ohne Probleme klarkommen, und dann waere eine Markierung auch weitesgehend unproblematisch.

herrmannj

#10
na das geht ja nicht um die Markierung sondern wie der String aufgebaut ist. Wenn man length() braucht dann weiß man das:)

Nichts desto trotz sind Deine 'Einwände' valide, das ist sehr Umfangreich.

btw grep -rnw . -e 'length(' liefert in /FHEM da schon einige :) Da sind sicher einige dabei die anders liefern als vom Autor gedacht wenn die mit einen é konfrontiert werden. Wozu auch immer dass dann führt

herrmannj

OK, ich berichte (meine learnings, seht das als Hypothese)

vorab: geht. (ohne das man fhemweb oder so anfassen muss)

Erste Erkenntnis: meine bisheriges Verständnis von perl UNICODE Strings bedarf offensichtlich einer Korrektur. Ich habe die bisher als eigenständige monolithische Einheit gesehen, etwas was scharf abgegrenzt und definiert ist. Ich glaube (tm) das ist falsch.

Ich hatte per httpget die Daten vom Server geholt, mir dann aus dem header das encoding geholt und war der Meinung dass es richtig ist an dieser Stelle auf Basis des encoding mit Hilfe von Encode::decode() zu erzeugen was perl als "unicode string" möchte. Das war falsch.

Was passiert wirklich? $data liefert einen byte stream. Wenn der server utf8 liefert (vmtl der häufigste Fall) an dieser Stelle also ein byte stream der bereits ein gültiges utf8 formt. Als nächstes gehen die Daten nach json decode (hier JSON::XS oder pure perl, je nach config). Der erwartet aber genau das (utf8 bytestream). Am "Ausgang" (jetzt als Object mit strings als k und/oder v) sind die (kv-) Strings 'UNICODE'. Noch genauer: die sind schon utf8 codiert (intern) und (wo nötig, mehr später) ist das utf8 flag (pro String individuell) gesetzt.

An dieser Stelle passieren haufenweise spannende Sachen. Erstens, die ganzen String Funktionen (length, pos, subst, regex, ...) funktionieren alle wie erwartet. length('jörg') ergibt zb 4. ABER: alles was FHEM IO ist funktioniert nicht. Also fhemweb, Log3, print etc. Das ist eine logische Kehrseite.Das lässt sich einfach lösen: man muss, im Modul, alles was mit IO zu tun hat (readingupdate zv) vorher so behandeln: "utf8::encode($r) if utf8::is_utf8($r);". Ab da passt beides, richtiges utf8 handling der Strings im Modul und richtige Ausgabe ohne weitere Änderungen.

Mein aktuelles Verständnis des blocks "UNICODE" ist jetzt so. UNICODE ist ein Sammelbegriff, quasi Obergruppe. Der einzelne UNICODE String ist dann entweder utf8, utf16 plus noch anderen. Wobei in context FHEM ja fast ausschließlich utf8 eine Rolle spielt

Zu den unterschiedlichen Fällen die man damit hat:
reines ASCII (automatisch auch gültiges UTF8)
Zeichenkette bestehend aus gültigem utf8 (bytestream):
* mit gesetzten utf8 Flag versteht perl die Sonderzeichen und behandelt sie in Operationen als _ein_ Zeichen.
* ohne gesetztes utf8 flag ist es zwar eine gültige utf8 Reihenfolge und perl behandelt jedes (jetzt) Zeichen einzeln. Aus einen 'ö' werden zwei Zeichen. (Das ist der Zustand den fhem im IO braucht)

Mein erster post wo ich geschrieben habe dass es mit 'ISO' dekodieren funktioniert, mit 'utf8' nicht, war weil: als ich auf den bytestream das utf8 decode losgelassen habe ist eine falsche "Doppelkodierung" dabei rausgekommen. Der war ja schon vom Server richtig als utf8 und ich habe das dadurch (falsch) nochmal kodiert. ISO ging weil das in diesem Fall ein NOOP war.

Der Trick ist also möglichst genau zu verstehen wann und wo was nötig ist. So richtig gaga wird es wenn man jemanden vermitteln will das es "richtig" ist wenn ein print oder ein Log3 das "richtige" falsch ins log wirft. ;) Ich würde mich anbieten im ersten Schritt ein Log4 beizusteuern was das berücksichtigt. (Log4 weil man es dann parallel hat und keine Interferenzen mit dem Bestand auftauchen).



rudolfkoenig

Soweit ich mich erinnere, wird beim Zusammenfuegen von utf8 und nicht utf8 Strings das Neue zwar mit dem utf8 Flag versehen, aber das nicht utf8 String wird vorher nicht konvertiert.
Bei Protokollen wie ZWave oder MQTT hat das Programm mit binaeren Bytestreams zu tun, da muss man genau entscheiden, ab wann die Binaerdaten als utf8 String zu behandeln sind. Dass diese Module length() brauchen, ist klar, aber ohne utf8 Flag.

Log4 waere ein Anfang bei der Umstellung auf eine interne utf8 Darstellung.
Bis es komplett umgestellt ist, wird es den Entwicklern schwer zu vermitteln sein, wann man ein String konvertieren muss, und wann nicht.
Selbst danach werden wir Probleme haben, nur andere.

herrmannj

ja absolut. Da gibt es kein one fits all.

Komplettumstellung? Die Message von mir sollte eigentlich lauten "braucht es nicht".

Ein Großteil der Module hat sowieso nichts mit Umlauten zu tun. Diejenigen die es betrifft können das Modul intern lösen. Bisher (zumindest bei mir, hat mich das letzte mal bei Smartvisu betroffen) war auch viel trial and error dabei. Daher der Versuch das zu strukturieren. Zum vermitteln, na da muss schon jeder selber schauen wo und wie.

Bisher kam ja immer mal wieder die Forderung auf das man da fhem.pl oder fhemweb anpacken muss weil aktuell sowas wie length und utf nicht geht. (hatte ich oben ja auch gesagt).

Da kann man ganz klar sagen, nö: muss man nicht, das geht sehr wohl. (so ist vmtl auch Deine Aussage gemeint)

herrmannj

der Vorschlag für Log4. Selbstredend 4 Parameter  ;) Als Test (wie oben angedacht) jedoch nicht geeignet weil es mit beiden Varianten (utf8 flag an oder aus) funktioniert. Vorteil, damit abwärtskompatibel

sub JsonMod_Logger {
my ($hash, $verbose, $message, @args) = @_;
my $name = $hash->{'NAME'};
# Unicode support for log files
utf8::encode($message) if utf8::is_utf8($message);
for my $i (0 .. scalar(@args)) {
utf8::encode($args[$i]) if utf8::is_utf8($args[$i]);
};
# https://forum.fhem.de/index.php/topic,109413.msg1034685.html#msg1034685
no if $] >= 5.022, 'warnings', qw( redundant missing );
Log3 ($name, $verbose, sprintf('[%s] '.$message, $name, @args));
return;
};