int packet_parse_http()

in erts/emulator/beam/packet_parser.c [643:843]


int packet_parse_http(const char* buf, int len, int* statep,
                      PacketCallbacks* pcb, void* arg)
{
    const char* ptr = buf;
    const char* p0;
    int n = len;

    /* remove trailing CRNL (accept NL as well) */
    if ((n >= 2) && (buf[n-2] == '\r'))
        n -= 2;
    else if ((n >= 1) && (buf[n-1] == '\n'))
        n -= 1;

    if (*statep == 0) {
        /* start-line = Request-Line | Status-Line */

        if (n >= 5 && (sys_strncmp(buf, "HTTP/", 5) == 0)) {
            int major  = 0;
            int minor  = 0;
            int status = 0;
            /* Status-Line = HTTP-Version SP
             *              Status-Code SP Reason-Phrase
             *              CRNL
             * HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT
             */
            ptr += 5;
            n -= 5;
            p0 = ptr;
            while (n && isdigit((int) *ptr)) {
                major = 10*major + (*ptr - '0');
                ptr++;
                n--;
            }
            if (ptr==p0 || !n || (*ptr != '.'))
                return -1;
            ptr++;
            n--;
            p0 = ptr;
            while (n && isdigit((int) *ptr)) {
                minor = 10*minor + (*ptr - '0');
                ptr++;
                n--;
            }
            if (ptr==p0) return -1;
            p0 = ptr;
            while (n && SP(ptr)) {
                ptr++; n--;
            }
            if (ptr==p0) return -1;
            
            while (n && isdigit((int) *ptr)) {
                status = 10*status + (*ptr - '0');
                ptr++;
                n--;
            }
            p0 = ptr;
            while (n && SP(ptr)) {
                ptr++; n--;
            }
            if (ptr==p0 && n>0) return -1;
            
            /* NOTE: the syntax allows empty reason phrases */
            (*statep) = !0;
            
            return pcb->http_response(arg, major, minor, status,
                                      ptr, n);
        }
        else {
            /* Request-Line = Method SP Request-URI SP HTTP-Version CRLF */
            http_atom_t* meth;
            const char* meth_ptr = buf;
            int         meth_len;
            PacketHttpURI uri;
            const char*   uri_ptr;
            int           uri_len;
            int major  = 0;
            int minor  = 0;
            unsigned long h = 0;

            while (n && !is_tspecial((unsigned char)*ptr)) {
                hash_update(h, (int)*ptr);
                ptr++;
                n--;
            }
            meth_len = ptr - meth_ptr;
            if (n == 0 || meth_len == 0 || !SP(ptr)) return -1;

            meth = http_hash_lookup(meth_ptr, meth_len, h,
                                    http_meth_hash, HTTP_METH_HASH_SIZE);

            while (n && SP(ptr)) {
                ptr++; n--;
            }
            uri_ptr = ptr;
            while (n && !SP(ptr)) {
                ptr++; n--;
            }
            if ((uri_len = (ptr - uri_ptr)) == 0)
                return -1;
            while (n && SP(ptr)) {
                ptr++; n--;
            }
            if (n == 0) {
                (*statep) = !0;
                http_parse_uri(&uri, uri_ptr, uri_len);
                return pcb->http_request(arg, meth, meth_ptr, meth_len,
                                         &uri, 0, 9);
            }
            if (n < 8)
                return -1;
            if (sys_strncmp(ptr, "HTTP/", 5) != 0)
                return -1;
            ptr += 5;
            n   -= 5;

            p0 = ptr;
            while (n && isdigit((int) *ptr)) {
                major = 10*major + (*ptr - '0');
                ptr++;
                n--;
            }            
            if (ptr==p0 || !n || (*ptr != '.'))
                return -1;
            ptr++;
            n--;
            p0 = ptr;
            while (n && isdigit((int) *ptr)) {
                minor = 10*minor + (*ptr - '0');
                ptr++;
                n--;
            }
            if (ptr==p0) return -1;

            (*statep) = !0;
            http_parse_uri(&uri, uri_ptr, uri_len);
            return pcb->http_request(arg, meth, meth_ptr, meth_len,
                                     &uri, major, minor);
        }
    }
    else {
        int up = 1;      /* make next char uppercase */
        http_atom_t* name;
        char name_buf[HTTP_MAX_NAME_LEN];
        const char* name_ptr = name_buf;
        int  name_len;
        unsigned long h;

        if (n == 0) {
            /* end of headers */
            *statep = 0;  /* reset state (for next request) */
            return pcb->http_eoh(arg);
        }
        h = 0;
        name_len = 0;
        while (!is_tspecial((unsigned char)*ptr)) {
            if (name_len < HTTP_MAX_NAME_LEN) {
                int c = *ptr;
                if (up) {
                    if (islower(c)) {
                        c = toupper(c);
                    }
                    up = 0;
                }
                else {
                    if (isupper(c))
                        c = tolower(c);
                    else if (c == '-')
                        up = 1;
                }                            
                name_buf[name_len] = c;
                hash_update(h, c);
            }
            name_len++;
            ptr++;
            if (--n == 0) return -1;
        }
        if (*ptr != ':') {
            return -1;
        }
        if (name_len <= HTTP_MAX_NAME_LEN) {
            name = http_hash_lookup(name_buf, name_len, h,
                                    http_hdr_hash, HTTP_HDR_HASH_SIZE);
        } 
        else {
            /* Is it ok to return original name without case adjustments? */
            name_ptr = buf;
            name = NULL;
        }
        ptr++;
        n--;
        /* Skip white space after ':' */
        while (n && SP(ptr)) {
            ptr++; n--;
        }
        return pcb->http_header(arg, name,
                                name_ptr, name_len,
                                buf, name_len,
                                ptr, n);
    }
    return -1;
}