From 1b5cc341a5da4c0a70d4205702e10dd3dcf6140a Mon Sep 17 00:00:00 2001 From: cpq Date: Mon, 8 Mar 2021 15:41:16 +0000 Subject: [PATCH] Add streaming client support, MG_EV_HTTP_CHUNK --- Makefile | 2 +- docs/README.md | 10 +- examples/http-streaming-client/Makefile | 22 + examples/http-streaming-client/ca.pem | 507 ++++++++++++++++++++++++ examples/http-streaming-client/main.c | 65 +++ mongoose.c | 119 ++++-- mongoose.h | 41 +- src/config.h | 4 - src/event.h | 35 +- src/http.c | 109 +++-- src/http.h | 1 + src/license.h | 3 +- test/unit_test.c | 131 ++++++ 13 files changed, 927 insertions(+), 122 deletions(-) create mode 100644 examples/http-streaming-client/Makefile create mode 100644 examples/http-streaming-client/ca.pem create mode 100644 examples/http-streaming-client/main.c diff --git a/Makefile b/Makefile index d8cf17ec..a5c25cad 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ SRCS = $(wildcard src/*.c) HDRS = $(wildcard src/*.h) -DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_HTTP_DEBUG_ENDPOINT=1 -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1 +DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1 CFLAGS ?= -W -Wall -Werror -Isrc -I. -O0 -g $(DEFS) $(TFLAGS) $(EXTRA) SSL ?= MBEDTLS CDIR ?= $(realpath $(CURDIR)) diff --git a/docs/README.md b/docs/README.md index b42b2631..d6bc09bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -223,7 +223,6 @@ Here is a list of build constants and their default values: |`MG_ENABLE_LOG` | 1 | Enable `LOG()` macro | |`MG_ENABLE_MD5` | 0 | Use native MD5 implementation | |`MG_ENABLE_DIRECTORY_LISTING` | 0 | Enable directory listing for HTTP server | -|`MG_ENABLE_HTTP_DEBUG_ENDPOINT` | 0 | Enable `/debug/info` debug URI | |`MG_ENABLE_SOCKETPAIR` | 0 | Enable `mg_socketpair()` for multi-threading | |`MG_ENABLE_SSI` | 0 | Enable serving SSI files by `mg_http_serve_dir()` | |`MG_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth | @@ -1241,3 +1240,12 @@ void mg_usleep(unsigned long usecs); ``` Block for a given number of microseconds. + +### mg\_crc32() + +```c +uint32_t mg_crc32(uint32_t crc, const uint8_t *buf, size_t len); +``` + +Calculate CRC32 checksum for a given buffer. An initial `crc` value should +be `0`. diff --git a/examples/http-streaming-client/Makefile b/examples/http-streaming-client/Makefile new file mode 100644 index 00000000..a3a3a1f5 --- /dev/null +++ b/examples/http-streaming-client/Makefile @@ -0,0 +1,22 @@ +PROG ?= example +CFLAGS ?= -DMG_ENABLE_LINES $(EXTRA) + +ifneq ($(MBEDTLS),) +CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include +CFLAGS += -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509 +endif + +ifneq ($(OPENSSL),) +CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include +CFLAGS += -L$(OPENSSL)/lib -lssl -lcrypto +endif + +all: $(PROG) + $(DEBUGGER) ./$(PROG) $(ARGS) + + +$(PROG): main.c + $(CC) ../../mongoose.c -I../.. -W -Wall $(CFLAGS) -o $(PROG) main.c + +clean: + rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb diff --git a/examples/http-streaming-client/ca.pem b/examples/http-streaming-client/ca.pem new file mode 100644 index 00000000..2e1af2f7 --- /dev/null +++ b/examples/http-streaming-client/ca.pem @@ -0,0 +1,507 @@ +Subject: O=Digital Signature Trust Co., CN=DST Root CA X3 +Not Before: Sep 30 21:12:19 2000 GMT +Not After : Sep 30 14:01:15 2021 GMT +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root +Not Before: May 12 18:46:00 2000 GMT +Not After : May 12 23:59:00 2025 GMT +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA +Not Before: May 21 04:00:00 2002 GMT +Not After : May 21 04:00:00 2022 GMT +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 ECC 256 bit SSL CA - G2 +Not Before: May 12 00:00:00 2015 GMT +Not After : May 11 23:59:59 2025 GMT +-----BEGIN CERTIFICATE----- +MIIEajCCA1KgAwIBAgIQP5KHvp0dpKN6nfYoLndaxDANBgkqhkiG9w0BAQsFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTUwNTEyMDAwMDAwWhcNMjUwNTExMjM1OTU5WjCBgDEL +MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD +VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBD +bGFzcyAzIEVDQyAyNTYgYml0IFNTTCBDQSAtIEcyMFkwEwYHKoZIzj0CAQYIKoZI +zj0DAQcDQgAEDxukkdfnrOfRTk63ZFvhj39uBNOrONtEt0Bcbb2WljffeYmGZ/ex +Hwie/WM7RoyfvVPoFdyXPiuBRq2Gfw4BOaOCAV0wggFZMC4GCCsGAQUFBwEBBCIw +IDAeBggrBgEFBQcwAYYSaHR0cDovL3Muc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYB +Af8CAQAwZQYDVR0gBF4wXDBaBgpghkgBhvhFAQc2MEwwIwYIKwYBBQUHAgEWF2h0 +dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5z +eW1jYi5jb20vcnBhMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNv +bS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKwYDVR0RBCQwIqQgMB4xHDAa +BgNVBAMTE1NZTUMtRUNDLUNBLXAyNTYtMjIwHQYDVR0OBBYEFCXwiuFLetkBlQrt +xlPxjHgf2fP4MB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBCwUAA4IBAQAMMGUXBaWTdaLxsTGtcB/naqjIQrLvoV9NG+7MoHpGd/69 +dZ/h2zOy7sGFUHoG/0HGRA9rxT/5w5GkEVIVkxtWyIWWq6rs4CTZt8Bej/KHYRbo +jtEDUkCTZSTLiCvguPyvinXgxy+LHT+PmdtEfXsvcdbeBSWUYpOsDYvD2hNtz9dw +Od5nBosMApmdxt+z7LQyZu8wMnfI1U6IMO+RWowxZ8uy0oswdFYd32l9xe+aAE/k +y9alLu/M9pvxiUKufqHJRgDBKA6uDjHLMPX+/nxXaNCPX3SI4KVZ1stHQ/U5oNlM +dHN9umAvlU313g0IgJrjsQ2nIdf9dsdP+6lrmP7s +-----END CERTIFICATE----- + +Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 +Not Before: Nov 8 00:00:00 2006 GMT +Not After : Jul 16 23:59:59 2036 GMT +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +Subject: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority +Not Before: Jun 29 17:06:20 2004 GMT +Not After : Jun 29 17:06:20 2034 GMT +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Note: Amazon ATS endpoint uses this (2018/12/18) +Subject: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority +Not Before: Jun 29 17:39:16 2004 GMT +Not After : Jun 29 17:39:16 2034 GMT +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +Not Before: Sep 1 00:00:00 2009 GMT +Not After : Dec 31 23:59:59 2037 GMT +Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2 +Not Before: Sep 1 00:00:00 2009 GMT +Not After : Dec 31 23:59:59 2037 GMT +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +Subject: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign +Not Before: Dec 15 08:00:00 2006 GMT +Not After : Dec 15 08:00:00 2021 GMT +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA +Not Before: Nov 10 00:00:00 2006 GMT +Not After : Nov 10 00:00:00 2031 GMT +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA +Not Before: Nov 10 00:00:00 2006 GMT +Not After : Nov 10 00:00:00 2031 GMT +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# *.azure-devices.de use this root. +Subject: C = DE, O = D-Trust GmbH, CN = D-TRUST Root Class 3 CA 2 2009 +Not Before: Nov 5 08:35:58 2009 GMT +Not After : Nov 5 08:35:58 2029 GMT +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# https://www.amazontrust.com/repository/ +Subject: C = US, O = Amazon, CN = Amazon Root CA 1 +Not Before: May 26 00:00:00 2015 GMT +Not After : Jan 17 00:00:00 2038 GMT +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Subject: C = US, O = Amazon, CN = Amazon Root CA 2 +Not Before: May 26 00:00:00 2015 GMT +Not After : May 26 00:00:00 2040 GMT +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +Not Before: May 26 00:00:00 2015 GMT +Not After : May 26 00:00:00 2040 GMT +Subject: C = US, O = Amazon, CN = Amazon Root CA 3 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Not Before: May 26 00:00:00 2015 GMT +Not After : May 26 00:00:00 2040 GMT +Subject: C = US, O = Amazon, CN = Amazon Root CA 4 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +Not Before: Nov 10 00:00:00 2006 GMT +Not After : Nov 10 00:00:00 2031 GMT +Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +Not Before: Nov 13 00:00:00 2012 GMT +Not After : Jan 19 03:14:07 2038 GMT +Subject: OU = GlobalSign ECC Root CA - R4, O = GlobalSign, CN = GlobalSign +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +Not Before: Nov 1 00:00:42 2018 GMT +Not After : Nov 1 00:00:42 2042 GMT +Subject: C = US, O = Google Trust Services LLC, CN = GTS LTSR +-----BEGIN CERTIFICATE----- +MIIBxTCCAWugAwIBAgINAfD3nVndblD3QnNxUDAKBggqhkjOPQQDAjBEMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzERMA8G +A1UEAxMIR1RTIExUU1IwHhcNMTgxMTAxMDAwMDQyWhcNNDIxMTAxMDAwMDQyWjBE +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzERMA8GA1UEAxMIR1RTIExUU1IwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATN +8YyO2u+yCQoZdwAkUNv5c3dokfULfrA6QJgFV2XMuENtQZIG5HUOS6jFn8f0ySlV +eORCxqFyjDJyRn86d+Iko0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUPv7/zFLrvzQ+PfNA0OQlsV+4u1IwCgYIKoZIzj0EAwID +SAAwRQIhAPKuf/VtBHqGw3TUwUIq7TfaExp3bH7bjCBmVXJupT9FAiBr0SmCtsuk +miGgpajjf/gFigGM34F9021bCWs1MbL0SA== +-----END CERTIFICATE----- diff --git a/examples/http-streaming-client/main.c b/examples/http-streaming-client/main.c new file mode 100644 index 00000000..8e2d5de9 --- /dev/null +++ b/examples/http-streaming-client/main.c @@ -0,0 +1,65 @@ +// Copyright (c) 2021 Cesanta Software Limited +// All rights reserved +// +// Example HTTP client. +// Instead of buffering the whole HTTP response in memory and wait for the +// MG_EV_HTTP_MSG event, this client catches MG_EV_HTTP_CHUNK events and +// prints them. This is useful to download large content without spooling +// everything to memory, or catch chunked content. +// +// You can change `s_url` from the command line by executing: ./example YOUR_URL +// +// To enable SSL/TLS for this client, build it like this: +// make MBEDTLS=/path/to/your/mbedtls/installation +// make OPENSSL=/path/to/your/openssl/installation + +#include "mongoose.h" + +// The very first web page in history. You can replace it from command line +static const char *s_url = "http://info.cern.ch"; + +// Print HTTP response and signal that we're done +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_CONNECT) { + // Connected to server. Extract host name from URL + struct mg_str host = mg_url_host(s_url); + + // If s_url is https://, tell client connection to use TLS + if (mg_url_is_ssl(s_url)) { + struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host}; + mg_tls_init(c, &opts); + } + + // Send request + mg_printf(c, + "GET %s HTTP/1.0\r\n" + "Host: %.*s\r\n" + "\r\n", + mg_url_uri(s_url), (int) host.len, host.ptr); + } else if (ev == MG_EV_HTTP_CHUNK) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + LOG(LL_INFO, ("%.*s", (int) hm->chunk.len, hm->chunk.ptr)); + mg_http_delete_chunk(c, hm); + if (hm->chunk.len == 0) *(bool *) fn_data = true; // Last chunk + } else if (ev == MG_EV_HTTP_MSG) { + // Response is received. Print it + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + LOG(LL_INFO, ("%.*s", (int) hm->body.len, hm->body.ptr)); + c->is_closing = 1; // Tell mongoose to close this connection + *(bool *) fn_data = true; // Tell event loop to stop + } else if (ev == MG_EV_ERROR) { + *(bool *) fn_data = true; // Error, tell event loop to stop + } +} + +int main(int argc, char *argv[]) { + struct mg_mgr mgr; // Event manager + bool done = false; // Event handler flips it to true + if (argc > 1) s_url = argv[1]; // Use URL from command line + mg_log_set("3"); // Set to 0 to disable debug + mg_mgr_init(&mgr); // Initialise event manager + mg_http_connect(&mgr, s_url, fn, &done); // Create client connection + while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop + mg_mgr_free(&mgr); // Free resources + return 0; +} diff --git a/mongoose.c b/mongoose.c index b96f1f45..76c630c7 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1097,9 +1097,6 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, t2[sizeof(t2) - 1] = '\0'; fp = mg_fopen(t2, "r"); } -#endif -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "<-F %s", t2); #endif if (is_index && fp == NULL) { #if MG_ENABLE_DIRECTORY_LISTING @@ -1177,64 +1174,103 @@ bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) { return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len); } -static void http_cb(struct mg_connection *c, int ev, void *ev_data, - void *fn_data) { +static size_t get_chunk_length(const char *buf, size_t len, size_t *ll) { + size_t i = 0, n; + while (i < len && buf[i] != '\r' && i != '\n') i++; + n = mg_unhexn((char *) buf, i); + while (i < len && (buf[i] == '\r' || i == '\n')) i++; + // LOG(LL_INFO, ("len %zu i %zu n %zu ", len, i, n)); + if (ll != NULL) *ll = i + 1; + if (i < len && i + n + 2 < len) return i + n + 3; + return 0; +} + +// Walk through all chunks in the chunked body. For each chunk, fire +// an MG_EV_HTTP_CHUNK event. +static void walkchunks(struct mg_connection *c, struct mg_http_message *hm, + int reqlen) { + size_t off = 0, ll; + while (off < c->recv.len - reqlen) { + char *buf = (char *) &c->recv.buf[reqlen]; + size_t memo = c->recv.len; + size_t cl = get_chunk_length(&buf[off], memo - reqlen - off, &ll); + // LOG(LL_INFO, ("len %zu off %zu cl %zu ll %zu", len, off, cl, ll)); + if (cl == 0) break; + hm->chunk = mg_str_n(&buf[off + ll], cl < ll + 2 ? 0 : cl - ll - 2); + mg_call(c, MG_EV_HTTP_CHUNK, hm); + // Increase offset only if user has not deleted this chunk + if (memo == c->recv.len) off += cl; + if (cl <= 5) { + // Zero chunk - last one. Set message length to indicate we've received + // everything, to fire MG_EV_HTTP_MSG + hm->message.len = off + reqlen; + hm->body.len = off; + break; + } + } +} + +static bool mg_is_chunked(struct mg_http_message *hm) { + struct mg_str needle = mg_str_n("chunked", 7); + struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding"); + return te != NULL && mg_strstr(*te, needle) != NULL; +} + +void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) { + struct mg_str ch = hm->chunk; + if (mg_is_chunked(hm)) { + ch.len += 4; // \r\n before and after the chunk + ch.ptr -= 2; + while (ch.ptr > hm->body.ptr && *ch.ptr != '\n') ch.ptr--, ch.len++; + } + { + const char *end = &ch.ptr[ch.len]; + size_t n = end - (char *) c->recv.buf; + if (c->recv.len > n) memmove((char *) ch.ptr, end, c->recv.len - n); + // LOG(LL_INFO, ("DELETING CHUNK: %zu %zu %zu\n%.*s", c->recv.len, n, + // ch.len, (int) ch.len, ch.ptr)); + } + c->recv.len -= ch.len; +} + +static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { struct mg_http_message hm; for (;;) { int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); + bool is_chunked = n > 0 && mg_is_chunked(&hm); if (ev == MG_EV_CLOSE) { hm.message.len = c->recv.len; hm.body.len = hm.message.len - (hm.body.ptr - hm.message.ptr); + } else if (is_chunked) { + walkchunks(c, &hm, n); } + // LOG(LL_INFO, + //("---->%d %d\n%.*s", n, is_chunked, (int) c->recv.len, c->recv.buf)); if (n < 0 && ev == MG_EV_READ) { LOG(LL_ERROR, ("%lu HTTP parse error", c->id)); c->is_closing = 1; break; } else if (n > 0 && (size_t) c->recv.len >= hm.message.len) { -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "<-[%.*s]", (int) hm.uri.len, - hm.uri.ptr); - if (mg_http_match_uri(&hm, "/debug/info")) { - struct mg_mgr *mgr = (struct mg_mgr *) fn_data; - struct mg_connection *x; - mg_printf(c, "%s\r\n", - "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"); - for (x = mgr->conns; x != NULL; x = x->next) { - // LOG(LL_INFO, ("%p %s", x->fd, x->label)); - mg_http_printf_chunk( - c, - "%-4p %-12s %04d.%04d/%04d.%04d" - " %d%d%d%d%d%d%d%d%d%d%d%d%d%d\n", - x->fd, x->label, x->recv.len, x->recv.size, x->send.len, - x->send.size, c->is_listening, c->is_client, c->is_accepted, - c->is_resolving, c->is_connecting, c->is_tls, c->is_tls_hs, - c->is_udp, c->is_websocket, c->is_hexdumping, c->is_draining, - c->is_closing, c->is_readable, c->is_writable); - } - mg_http_write_chunk(c, "", 0); - mg_iobuf_delete(&c->recv, hm.message.len); - continue; - } -#endif mg_call(c, MG_EV_HTTP_MSG, &hm); mg_iobuf_delete(&c->recv, hm.message.len); } else { + if (n > 0 && !is_chunked) { + hm.chunk = mg_str_n((char *) &c->recv.buf[n], c->recv.len - n); + mg_call(c, MG_EV_HTTP_CHUNK, &hm); + } break; } } } - (void) fn_data; - (void) ev_data; + (void) fnd; + (void) evd; } struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data) { struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); if (c != NULL) c->pfn = http_cb; -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "->%s", url); -#endif return c; } @@ -1242,9 +1278,6 @@ struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data) { struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); if (c != NULL) c->pfn = http_cb; -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - if (c != NULL) snprintf(c->label, sizeof(c->label) - 1, "<-LSN"); -#endif return c; } @@ -4069,6 +4102,16 @@ int64_t mg_to64(struct mg_str str) { return result * neg; } +uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) { + int i; + crc = ~crc; + while (len--) { + crc ^= *(unsigned char *) buf++; + for (i = 0; i < 8; i++) crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1; + } + return ~crc; +} + double mg_time(void) { #if MG_ARCH == MG_ARCH_WIN32 SYSTEMTIME sysnow; diff --git a/mongoose.h b/mongoose.h index 7b24ec85..f033e287 100644 --- a/mongoose.h +++ b/mongoose.h @@ -333,10 +333,6 @@ typedef int socklen_t; #define MG_ENABLE_DIRECTORY_LISTING 0 #endif -#ifndef MG_ENABLE_HTTP_DEBUG_ENDPOINT -#define MG_ENABLE_HTTP_DEBUG_ENDPOINT 0 -#endif - #ifndef MG_ENABLE_SOCKETPAIR #define MG_ENABLE_SOCKETPAIR 0 #endif @@ -436,6 +432,7 @@ bool mg_globmatch(const char *pattern, int plen, const char *s, int n); bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v); uint16_t mg_ntohs(uint16_t net); uint32_t mg_ntohl(uint32_t net); +uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); char *mg_hexdump(const void *buf, size_t len); char *mg_hex(const void *buf, int len, char *dst); void mg_unhex(const char *buf, int len, unsigned char *to); @@ -572,23 +569,24 @@ void mg_call(struct mg_connection *c, int ev, void *ev_data); void mg_error(struct mg_connection *c, const char *fmt, ...); enum { - MG_EV_ERROR, // Error char *error_message - MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis - MG_EV_RESOLVE, // Host name is resolved NULL - MG_EV_CONNECT, // Connection established NULL - MG_EV_ACCEPT, // Connection accepted NULL - MG_EV_READ, // Data received from socket struct mg_str * - MG_EV_WRITE, // Data written to socket int *num_bytes_written - MG_EV_CLOSE, // Connection closed NULL - MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * - MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * - MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * - MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * - MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * - MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * - MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code - MG_EV_SNTP_TIME, // SNTP time received struct timeval * - MG_EV_USER, // Starting ID for user events + MG_EV_ERROR, // Error char *error_message + MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis + MG_EV_RESOLVE, // Host name is resolved NULL + MG_EV_CONNECT, // Connection established NULL + MG_EV_ACCEPT, // Connection accepted NULL + MG_EV_READ, // Data received from socket struct mg_str * + MG_EV_WRITE, // Data written to socket int *num_bytes_written + MG_EV_CLOSE, // Connection closed NULL + MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * + MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message * + MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * + MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * + MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * + MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * + MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * + MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code + MG_EV_SNTP_TIME, // SNTP time received struct timeval * + MG_EV_USER, // Starting ID for user events }; @@ -699,6 +697,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *); int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); +void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm); struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, diff --git a/src/config.h b/src/config.h index 2a83c945..891bc333 100644 --- a/src/config.h +++ b/src/config.h @@ -46,10 +46,6 @@ #define MG_ENABLE_DIRECTORY_LISTING 0 #endif -#ifndef MG_ENABLE_HTTP_DEBUG_ENDPOINT -#define MG_ENABLE_HTTP_DEBUG_ENDPOINT 0 -#endif - #ifndef MG_ENABLE_SOCKETPAIR #define MG_ENABLE_SOCKETPAIR 0 #endif diff --git a/src/event.h b/src/event.h index 0c788255..10d9ebdc 100644 --- a/src/event.h +++ b/src/event.h @@ -7,21 +7,22 @@ void mg_call(struct mg_connection *c, int ev, void *ev_data); void mg_error(struct mg_connection *c, const char *fmt, ...); enum { - MG_EV_ERROR, // Error char *error_message - MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis - MG_EV_RESOLVE, // Host name is resolved NULL - MG_EV_CONNECT, // Connection established NULL - MG_EV_ACCEPT, // Connection accepted NULL - MG_EV_READ, // Data received from socket struct mg_str * - MG_EV_WRITE, // Data written to socket int *num_bytes_written - MG_EV_CLOSE, // Connection closed NULL - MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * - MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * - MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * - MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * - MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * - MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * - MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code - MG_EV_SNTP_TIME, // SNTP time received struct timeval * - MG_EV_USER, // Starting ID for user events + MG_EV_ERROR, // Error char *error_message + MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis + MG_EV_RESOLVE, // Host name is resolved NULL + MG_EV_CONNECT, // Connection established NULL + MG_EV_ACCEPT, // Connection accepted NULL + MG_EV_READ, // Data received from socket struct mg_str * + MG_EV_WRITE, // Data written to socket int *num_bytes_written + MG_EV_CLOSE, // Connection closed NULL + MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * + MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message * + MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * + MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * + MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * + MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * + MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * + MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code + MG_EV_SNTP_TIME, // SNTP time received struct timeval * + MG_EV_USER, // Starting ID for user events }; diff --git a/src/http.c b/src/http.c index 5f7c825f..5b28b666 100644 --- a/src/http.c +++ b/src/http.c @@ -677,9 +677,6 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, t2[sizeof(t2) - 1] = '\0'; fp = mg_fopen(t2, "r"); } -#endif -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "<-F %s", t2); #endif if (is_index && fp == NULL) { #if MG_ENABLE_DIRECTORY_LISTING @@ -757,64 +754,103 @@ bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) { return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len); } -static void http_cb(struct mg_connection *c, int ev, void *ev_data, - void *fn_data) { +static size_t get_chunk_length(const char *buf, size_t len, size_t *ll) { + size_t i = 0, n; + while (i < len && buf[i] != '\r' && i != '\n') i++; + n = mg_unhexn((char *) buf, i); + while (i < len && (buf[i] == '\r' || i == '\n')) i++; + // LOG(LL_INFO, ("len %zu i %zu n %zu ", len, i, n)); + if (ll != NULL) *ll = i + 1; + if (i < len && i + n + 2 < len) return i + n + 3; + return 0; +} + +// Walk through all chunks in the chunked body. For each chunk, fire +// an MG_EV_HTTP_CHUNK event. +static void walkchunks(struct mg_connection *c, struct mg_http_message *hm, + int reqlen) { + size_t off = 0, ll; + while (off < c->recv.len - reqlen) { + char *buf = (char *) &c->recv.buf[reqlen]; + size_t memo = c->recv.len; + size_t cl = get_chunk_length(&buf[off], memo - reqlen - off, &ll); + // LOG(LL_INFO, ("len %zu off %zu cl %zu ll %zu", len, off, cl, ll)); + if (cl == 0) break; + hm->chunk = mg_str_n(&buf[off + ll], cl < ll + 2 ? 0 : cl - ll - 2); + mg_call(c, MG_EV_HTTP_CHUNK, hm); + // Increase offset only if user has not deleted this chunk + if (memo == c->recv.len) off += cl; + if (cl <= 5) { + // Zero chunk - last one. Set message length to indicate we've received + // everything, to fire MG_EV_HTTP_MSG + hm->message.len = off + reqlen; + hm->body.len = off; + break; + } + } +} + +static bool mg_is_chunked(struct mg_http_message *hm) { + struct mg_str needle = mg_str_n("chunked", 7); + struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding"); + return te != NULL && mg_strstr(*te, needle) != NULL; +} + +void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) { + struct mg_str ch = hm->chunk; + if (mg_is_chunked(hm)) { + ch.len += 4; // \r\n before and after the chunk + ch.ptr -= 2; + while (ch.ptr > hm->body.ptr && *ch.ptr != '\n') ch.ptr--, ch.len++; + } + { + const char *end = &ch.ptr[ch.len]; + size_t n = end - (char *) c->recv.buf; + if (c->recv.len > n) memmove((char *) ch.ptr, end, c->recv.len - n); + // LOG(LL_INFO, ("DELETING CHUNK: %zu %zu %zu\n%.*s", c->recv.len, n, + // ch.len, (int) ch.len, ch.ptr)); + } + c->recv.len -= ch.len; +} + +static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { struct mg_http_message hm; for (;;) { int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); + bool is_chunked = n > 0 && mg_is_chunked(&hm); if (ev == MG_EV_CLOSE) { hm.message.len = c->recv.len; hm.body.len = hm.message.len - (hm.body.ptr - hm.message.ptr); + } else if (is_chunked) { + walkchunks(c, &hm, n); } + // LOG(LL_INFO, + //("---->%d %d\n%.*s", n, is_chunked, (int) c->recv.len, c->recv.buf)); if (n < 0 && ev == MG_EV_READ) { LOG(LL_ERROR, ("%lu HTTP parse error", c->id)); c->is_closing = 1; break; } else if (n > 0 && (size_t) c->recv.len >= hm.message.len) { -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "<-[%.*s]", (int) hm.uri.len, - hm.uri.ptr); - if (mg_http_match_uri(&hm, "/debug/info")) { - struct mg_mgr *mgr = (struct mg_mgr *) fn_data; - struct mg_connection *x; - mg_printf(c, "%s\r\n", - "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"); - for (x = mgr->conns; x != NULL; x = x->next) { - // LOG(LL_INFO, ("%p %s", x->fd, x->label)); - mg_http_printf_chunk( - c, - "%-4p %-12s %04d.%04d/%04d.%04d" - " %d%d%d%d%d%d%d%d%d%d%d%d%d%d\n", - x->fd, x->label, x->recv.len, x->recv.size, x->send.len, - x->send.size, c->is_listening, c->is_client, c->is_accepted, - c->is_resolving, c->is_connecting, c->is_tls, c->is_tls_hs, - c->is_udp, c->is_websocket, c->is_hexdumping, c->is_draining, - c->is_closing, c->is_readable, c->is_writable); - } - mg_http_write_chunk(c, "", 0); - mg_iobuf_delete(&c->recv, hm.message.len); - continue; - } -#endif mg_call(c, MG_EV_HTTP_MSG, &hm); mg_iobuf_delete(&c->recv, hm.message.len); } else { + if (n > 0 && !is_chunked) { + hm.chunk = mg_str_n((char *) &c->recv.buf[n], c->recv.len - n); + mg_call(c, MG_EV_HTTP_CHUNK, &hm); + } break; } } } - (void) fn_data; - (void) ev_data; + (void) fnd; + (void) evd; } struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data) { struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); if (c != NULL) c->pfn = http_cb; -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "->%s", url); -#endif return c; } @@ -822,8 +858,5 @@ struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data) { struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); if (c != NULL) c->pfn = http_cb; -#if MG_ENABLE_HTTP_DEBUG_ENDPOINT - if (c != NULL) snprintf(c->label, sizeof(c->label) - 1, "<-LSN"); -#endif return c; } diff --git a/src/http.h b/src/http.h index 67ceb607..13a4ae06 100644 --- a/src/http.h +++ b/src/http.h @@ -31,6 +31,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *); int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); +void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm); struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, diff --git a/src/license.h b/src/license.h index c76a96ea..47a86452 100644 --- a/src/license.h +++ b/src/license.h @@ -1,5 +1,5 @@ // Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2020 Cesanta Software Limited +// Copyright (c) 2013-2021 Cesanta Software Limited // All rights reserved // // This software is dual-licensed: you can redistribute it and/or modify @@ -14,4 +14,3 @@ // // Alternatively, you can license this software under a commercial // license, as set out in . - diff --git a/test/unit_test.c b/test/unit_test.c index 871a71d9..3c5f025f 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1151,8 +1151,139 @@ static void test_util(void) { } } +static void test_crc32(void) { + // echo -n aaa | cksum -o3 + ASSERT(mg_crc32(0, 0, 0) == 0); + ASSERT(mg_crc32(0, "a", 1) == 3904355907); + ASSERT(mg_crc32(0, "abc", 3) == 891568578); + ASSERT(mg_crc32(mg_crc32(0, "ab", 2), "c", 1) == 891568578); +} + +// Streaming server event handler. +// Send one chunk immediately, then drain, then send two chunks in one go +static void eh2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_HTTP_MSG) { + mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + mg_http_printf_chunk(c, "chunk 0"); + c->label[0] = 1; + } else if (ev == MG_EV_POLL) { + if (c->label[0] > 0 && c->send.len == 0) c->label[0]++; + if (c->label[0] > 10 && c->label[0] != 'x') { + mg_http_printf_chunk(c, "chunk 1"); + mg_http_printf_chunk(c, "chunk 2"); + mg_http_printf_chunk(c, ""); + c->label[0] = 'x'; + } + } + (void) ev_data; + (void) fn_data; +} + +// Non-streaming client event handler. Make sure we've got full body +static void eh3(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_CONNECT) { + mg_printf(c, "GET / HTTP/1.0\n\n"); + } else if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + // LOG(LL_INFO, ("----> [%.*s]", (int) hm->body.len, hm->body.ptr)); + c->is_closing = 1; + *(uint32_t *) fn_data = mg_crc32(0, hm->body.ptr, hm->body.len); + } +} + +// Streaming client event handler. Make sure we've got all chunks +static void eh4(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_CONNECT) { + mg_printf(c, "GET / HTTP/1.0\n\n"); + } else if (ev == MG_EV_HTTP_CHUNK) { + uint32_t *crc = (uint32_t *) c->label; + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + *crc = mg_crc32(*crc, hm->chunk.ptr, hm->chunk.len); + } else if (ev == MG_EV_HTTP_MSG) { + uint32_t *crc = (uint32_t *) c->label; + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + // LOG(LL_INFO, ("MSG [%.*s]", (int) hm->body.len, hm->body.ptr)); + c->is_closing = 1; + *(int *) fn_data = mg_crc32(*crc, hm->body.ptr, hm->body.len); + } + (void) ev_data; +} + +// Streaming client event handler. Delete chunks as they arrive +static void eh5(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_CONNECT) { + mg_printf(c, "GET / HTTP/1.0\n\n"); + } else if (ev == MG_EV_HTTP_CHUNK) { + uint32_t *crc = (uint32_t *) c->label; + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + *crc = mg_crc32(*crc, hm->chunk.ptr, hm->chunk.len); + LOG(LL_INFO, ("CHUNK [%.*s]", (int) hm->chunk.len, hm->chunk.ptr)); + mg_http_delete_chunk(c, hm); + } else if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + uint32_t *crc = (uint32_t *) c->label; + c->is_closing = 1; + *(int *) fn_data = mg_crc32(*crc, hm->body.ptr, hm->body.len); + LOG(LL_INFO, ("MSG [%.*s]", (int) hm->body.len, hm->body.ptr)); + } + (void) ev_data; +} + +static void test_http_chunked(void) { + struct mg_mgr mgr; + const char *url = "http://127.0.0.1:12344"; + uint32_t i, done = 0; + mg_mgr_init(&mgr); + mg_http_listen(&mgr, url, eh2, NULL); + + mg_http_connect(&mgr, url, eh3, &done); + for (i = 0; i < 50 && done == 0; i++) mg_mgr_poll(&mgr, 1); + ASSERT(i < 50); + { + const char *data = + "7\r\nchunk 0\r\n" + "7\r\nchunk 1\r\n" + "7\r\nchunk 2\r\n" + "0\r\n\r\n"; + ASSERT(done == mg_crc32(0, data, strlen(data))); + } + + done = 0; + mg_http_connect(&mgr, url, eh4, &done); + for (i = 0; i < 50 && done == 0; i++) mg_mgr_poll(&mgr, 1); + { + const char *data = + "chunk 0" + "chunk 0" + "chunk 1" + "chunk 2" + "7\r\nchunk 0\r\n" + "7\r\nchunk 1\r\n" + "7\r\nchunk 2\r\n" + "0\r\n\r\n"; + ASSERT(done == mg_crc32(0, data, strlen(data))); + } + + done = 0; + mg_http_connect(&mgr, url, eh5, &done); + for (i = 0; i < 50 && done == 0; i++) mg_mgr_poll(&mgr, 1); + { + const char *data = + "chunk 0" + "chunk 1" + "chunk 2"; + ASSERT(done == mg_crc32(0, data, strlen(data))); + } + + mg_mgr_free(&mgr); + ASSERT(mgr.conns == NULL); + exit(0); +} + int main(void) { mg_log_set("3"); + test_crc32(); + test_http_chunked(); test_http_parse(); test_util(); test_sntp();