1. C++ / Говнокод #25695

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    std::string makeFormContent(const std::string & album,
                                const std::wstring & filename,
                                const std::string & boundary)
    {
        static const std::string DELIM = "\r\n";
        std::ostringstream ss;
        std::ifstream file(filename, std::ios::binary);
    
    
        ss << boundary << DELIM;
        ss << "Content-Disposition: form-data; name=\"album\"" << DELIM << DELIM;
        ss << album << DELIM;
    
        ss << boundary << DELIM;
        ss << "Content-Disposition: form-data; name=\"image\"; filename=\"image\"" << DELIM << DELIM;
        ss << file.rdbuf() << DELIM;
    
        ss << boundary << DELIM << "--";
    
        return ss.str();
    }

    Заебали. Куча HTTP-либ под кресты, а банально сделать POST-запрос с multipart/form-data без кучи ебли нельзя. Приходится самому составлять, лол.

    Именно поэтому я за «requests.post(url, data=data, files=files)».

    Запостил: gost, 02 Июля 2019

    Комментарии (120) RSS

    • Переведи на "PHP".
      Ответить
      • <?php
        define('DELIM', "\r\n");
        
        makeFormContent($album, $filename, $boundary) {
            $file = file_get_contents($filename);
        
            $ss = $boundary . DELIM;
            $ss .= 'Content-Disposition: form-data; name="album"' . DELIM . DELIM;
            $ss .= $album . DELIM;
        
            $ss .= $boundary . DELIM;
            $ss .= 'Content-Disposition: form-data; name="image"; filename="image"' . DELIM . DELIM;
            $ss .= $file . DELIM;
        
            $ss .= $boundary . DELIM . "--";
        
            return $ss;
        }
        Ответить
        • Кстати, а что будет, если в файле случайно встретится строка, совпадающая со значением параметра boundary?
          Ответить
          • Придётся выбирать другое boundary :(
            Ответить
            • Значит, перед отправкой нужно сканировать весь файл на предмет того, не встретится ли в нём boundary, и, если встретилось, генерировать новое boundary и снова проверять? Или обычно проверяют задним числом, когда сервер уже получил обрезанный файл?
              Ответить
              • Да просто генерят какую-нибудь достаточно длинную хуйню и надеются, что её там нет. Один хер те же 128 бит совпадают чуть реже чем никогда.
                Ответить
                • И весь веб держится на такой тонкой ниточке???

                  В то время, когда космические корабли бороздят Большой театр, когда выходят новые версии TLS с новыми протоколами шифрования и подписи, формы для отправки файлов собираются таким алхимическим способом???
                  Ответить
                  • Предлагаю строить из файла префиксное дерево и генерировать гарантированно уникальную boundary за logN.
                    Ответить
                    • показать все, что скрытоvanished
                      Ответить
                      • Ну например хранить в каждой ноде глубину поддерева и идти от корня в самую мелкую сторону. Там ты найдёшь одну из кратчайших последовательностей, которых в файле нет.
                        Ответить
                    • Предлагаю приписать к файлу любую букву и считать полученную строку границей. Ибо её точно нет в файле.
                      Ответить
                      • Нельзя совсем любую. Нельзя приписывать байт, совпадающий с первым байтом файла, потому что тогда парсер решит, что мы передали пустой файл.

                        Например, содержимое файла ABC. Если мы возьмём в качестве границы ABCA, то получим:
                        ABCA
                        ABCABCA
                        Встретив второй раз ABCA, парсер решит, что это конец. А если добавим любой другой символ, то облома не будет:
                        ABCD
                        ABCABCD
                        Ответить
                      • Предлагаю оптимизацию: изменять первый символ файла и считать полученную строку границей. Сэкономим один байт.
                        Ответить
                  • Шифрование и подписи тоже держатся на вероятности... Да и тот же git.

                    Более того, вся электроника построена на вероятности. Метастабильность при передаче данных между разными частотными доменами убрать никак нельзя, можно только загнать вероятность в угол и делать вид, что всё заебись... И что один случай за миллиарды лет никто не заметит.
                    Ответить
          • показать все, что скрытоvanished
            Ответить
            • Content-Length же можно указать для каждого файла? Тогда не надо будет искать boundary в его кишках.
              Ответить
              • Ещё можно закодировать файл в Base64 или во что-нибудь ещё (urlencode, например, но заэскейпить каждый символ). Тогда выбрать boundary будет легче.
                Ответить
                • Ну это же лишний трафик. Царь не одобрит.
                  Ответить
                • Я как-то имплементировал эдакий «Base250»: кодирование произвольных байтиков набором из 250 возможных значений. Сделал царское кодирование: декодер читает байты, пока не встретится FF; следующий за FF байт — «специальный»: три его нижних бита определяют индекс в массиве «запрещённых» байт, а пять верхних — расстояние до следующего специального байта. В декодированном потоке специальный байт заменяется на запрещённый, индекс которого указан в нижних битах специального. В результате в идеальном случае (в исходном потоке запрещённые байты находятся на расстоянии не более ≈30) мы получаем оверхед ровно в 1 байт!
                  Ответить
                  • Дык если ты можешь передавать расстояние, то у тебя стрим достаточно стабилен и не ему не нужны все эти специальные символы. Так что и тупые чанки по 255 байт с длиной в первом байте сойдут. И ёбли на порядок меньше :)
                    Ответить
                  • Или специальный байт тоже никогда не может принять запрещённое значение?
                    Ответить
                    • Да, так совпало, что он гарантированно получается разрешённым. Суть в том, что надо максимально компактно передать стрим бинарных данных через среду, в которой несколько байт к передаче запрещены (\x00, например).
                      Ответить
                      • Сишные строки что ли?
                        Ответить
                        • Они, плюс дополнительная фильтрация от приложения.
                          Ответить
                      • Ну кстати прикольно получается. В стриме с рандомом один из 8 запрещённых символов будет выпадать с матожиданием как раз в 32 байта... А в непожатом будет дохуя нулей и всегда будет за что зацепиться.
                        Ответить
                      • Или всё-таки арифметический кодек с его ровным и гарантированным оверхедом в 0.43% будет лучше для пожатых данных?
                        Ответить
                        • Арифметический кодек?
                          Ответить
                          • https://en.wikipedia.org/wiki/Arithmetic_coding
                            https://en.wikipedia.org/wiki/Range_encoding
                            Ответить
                            • Спасибо, очень элегантная штука. У меня ещё была проблема в очень сильной ограниченности размера — одно сообщение должно было умещаться в 150 символов, поэтому каждый байтик метаданных существенно увеличивал оверхед.
                              Ответить
                              • Там не надо метаданных, оверхед будет те самые 0.43%. Ну с округлением вверх до байта.
                                Ответить
                              • > шутка
                                > 150 символов
                                Ну вот смотри 150 символов по log2(250) бит это 1194 с лишним бита аля 149 байт (если округлить вниз). Куда плотнее то? И это на любых данных, тут не будет никаких ограничений на "нули не реже 30".
                                Ответить
                                • Так и становятся битоёбом…
                                  Ответить
                                  • > битоёбом
                                    Напротив, арифметическое кодирование сбрасывает оковы битоёбства!

                                    Зачем, к примеру, тратить на число от 0 до 5 целых три бита, если достаточно 2.59?
                                    Ответить
                                  • А если какой-нибудь флажок включается очень редко, то зачем мне тратить на false целый бит, если достаточно небольшой части бита (true при этом займёт несколько бит, но и хер с ним)?

                                    Долой неделимость битов!
                                    Ответить
                              • > если округлить вниз
                                А если не округлять, то 149 байт и целых 2 бита, в которые ты сможешь положить что-то полезное!
                                Ответить
                          • Если мы не знаем вероятности, это будет выглядеть тупо как перевод дробного числа от 0 до 1 из 256-ричной системы в 250-ричную.
                            Ответить
                          • Ну или можно делить твой файл на 250 в столбик и выписывать частные в выхлоп энкодера.
                            Ответить
                  • > в нижних битах
                    > не более 30
                    Но ведь запрещённых символов 6, а не 8? Поэтому из 7.96 бит мы потратим 2.58 и у нас останется 5.38 бит чтобы записать расстояние от 0 до 40!

                    Для этого достаточно заюзать (dist * 6) + code вместо (dist << 3) | code. Ну и ремапнуть полученное число от 0 до 245 в разрешённые символы по табличке.
                    Ответить
                    • Отличное решение, так оверхед на рандомных данных вообще будет околонулевой!
                      Ответить
                      • Вообще говоря нет, ты теряешь 1 байт на каждом разрыве цепочки, а это не так уж редко, как бы не 1/2 на каждом спецсимволе... Надо считать, в общем. Но лень.
                        Ответить
      • Welcher Einreißhaken )))
        Ответить
    • показать все, что скрытоvanished
      Ответить
      • «ЗЩЫЕ-запросы» бывают с «аппликация/х-шшш-форм-урленкодед» и с «мультипарт/форм-дата». Первый вариант браузеры собирают для <форм метод="пост">, когда не нужно передавать файлы (когда передаются только текстовые поля и галочки), второй вариант браузеры собирают, когда нужно передавать файлы (когда присутствует поле <инпут тупе="филе">).
        Ответить
      • auto rest_client = restc_cpp::RestClient::Create();
        
        bool exception = false;
        auto promise = rest_client->ProcessWithPromiseT<std::string>([&](restc_cpp::Context & ctx) {
            std::string boundary = "41d148306c0b2d3d0dd779d462883b7c";
            auto content = makeFormContent(album, imagePath, "--" + boundary);
            try {
                auto response = restc_cpp::RequestBuilder(ctx)
                    .Post("https://api.imgur.com/3/image")
                    .AddHeader("Authorization", "Bearer " + std::string(authToken))
                    .Header("Content-Type", "multipart/form-data; boundary=" + boundary)
                    .Data(content)
                    .Execute();
                return response->GetBodyAsString();
            }
            catch (restc_cpp::RestcCppException e) {
                exception = true;
                return std::string(e.what());
            }
        });
        auto response = promise.get();

        Какая обработка исключений )))
        Ответить
        • > bearer
          Bear -> bearer -> bearest.
          Ответить
        • Скопировал экцепшон тебе за щеку, проверь.
          Ответить
        • показать все, что скрытоvanished
          Ответить
        • >2k19
          >restc_cpp
          В кресты-бусты до сих пор не завезли удобных апи с быдлерами?
          Ответить
          • Есть какой-то «boost::http», но его дока в текстовом виде весит 250 килобайт и написана в лучших традициях крестового Стандарта. Короткие туториалы по нему не гуглятся (видимо, кроме создателей его так никто и не осилил), только красивые решения в таком духе:
            request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";
            request_stream << "Host: " << argv[1] << "\r\n";
            request_stream << "Accept: */*\r\n";
            request_stream << "Connection: close\r\n\r\n";

            Багор, как есть багор!
            Ответить
            • Ксат, почему ББ и ЮЮ, Они не могли зопятую перегрузить?
              Ответить
              • Лол, всегда думал, что запятая в крестах не перегружается. Это ж такие поехавшие DSL'и можно наметапрограммировать!
                Ответить
                • class A{};
                  
                  const A& operator, (const A& a, const char* s)
                  {
                      std::cout << s;
                      return a;
                  }

                  Забавные чудеса крестотипизации: cтоит проебать один const и оператор применяется только первый раз, но всё успешно конпелируется, ведь запятая работает со всеми типами, и я долго не мог понять в чем дело.

                  Тогда почему он вообще применяется, если я написал "A a", а не "const A a"?
                  Ответить
            • > <<
              Fluent интерфейс же.
              Ответить
            • Высирать куски протокола в стрим. Мдяяяя.
              Зато низкоуровнево.
              Ответить
            • показать все, что скрытоvanished
              Ответить
        • показать все, что скрытоvanished
          Ответить
    • Можно собрать запрос с помощью «libcurl», но это уже будет не по-крестовому, а по-сишному:
      https://curl.haxx.se/libcurl/c/multi-post.html
      Ответить
    • А если нам нужно передать файл на несколько гигабайтиков (образ DVD, например), то как лучше всего собирать подобную форму?
      Ответить
      • Для этого надо, чтобы сервер поддерживал POST-запросы большого размера (по-умолчанию большинство серверов поддерживают 2 GB). Адекватное решение — пересылать файл по частям через «JavaScript».
        Ответить
        • > POST-запросы большого размера
          Вообще странно, что в том же PHP сначала сервак полностью закачивает файл и только потом скрипт получает управление и решает, что делать с этим файлом (или вообще выбрасывает нахуй, лол).
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • Х.з., я ж не настоящий сварщик. Но в джавке (spring?) вроде можно было самому читать стрим от клиента. Если мне не привиделось.
              Ответить
              • Оно из стандартной либы на InputStreamax работало испокон веков.

                И клиент (java.net.URLConnection) и сервлеты (https://docs.oracle.com/javaee/6/api/javax/servlet/ServletRequest.html).

                Spring только даёт обёртки: restClient (не слишком удобный) и инъекцию этих requestов в аннотированные методы.
                Впрочем всё это и разные jersey умеют.
                Ответить
        • Это правда, что при некоторых видах авторизации произойдёт чудо и файл будет загружен дважды: один раз с 401, второй раз с auth header?
          Ответить
          • Если авторизацию делает скрипт, а не сервер - вполне возможно...
            Ответить
          • Кстати, а браузеры умеют отправлять Expect: 100-continue чтобы не заливать весь файл вслепую?
            Ответить
            • https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100

              Уметь видимо умеют, отправляют ли надо смотреть в каждом конкретном случае
              Ответить
              • Судя по хромовскому багтрекеру - хуй там. Хром вообще отваливается по таймауту если инет медленный и файл не успел залиться :)
                Ответить
                • Жирно! Повышение энтропии от гугла
                  Ответить
                  • Вроде и у фф такая же херня. Только curl отправляет этот expect.
                    Ответить
                    • > ДЦП'шники ищут, как это отключить

                      - Холмс, но зачем?!
                      Ответить
                      • Мешает, видимо. Надо же сначала отправить 100 Continue и только потом получишь тело.
                        Ответить
                        • Понятно.

                          А я чего спросил, не повезло попасть на кровавый ъ-прайз, там коллега жаловался, что сервак работает именно вот так, как я выше написал. А они там гоняют достаточно толстые жисоны по несколько десятков мегабайт.
                          Ответить
                          • Ну можно попробовать HEAD'ом пингануть, а потом полноценный запрос...
                            Ответить
                            • Ну или OPTIONS, прокатит же? Но что-то у них и так не работало, где-то затесались жопоруки
                              Ответить
    • выкинь нахуй это говно, убери плюсики в конце
      Ответить

    Добавить комментарий