src/server/daemon/httpparser.c

Fri, 06 Feb 2026 17:07:58 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 06 Feb 2026 17:07:58 +0100
changeset 665
b8d5b797d090
parent 663
bd116bd44926
permissions
-rw-r--r--

add first http client code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>

#include "httpparser.h"
#include "../public/nsapi.h"
//include "request.h"


HttpParser* http_parser_new(HTTPRequest *request) {
    return http_parser_new2(0, request->netbuf, request->headers);
}

HttpParser* http_parser_new2(int type, netbuf *netbuf, HeaderArray *headers) {
    HttpParser *parser = calloc(1, sizeof(HttpParser));
    parser->type = type;
    parser->netbuf = netbuf;
    parser->headers = headers;
    
    parser->state = 0;
    parser->start_line.ptr = (char*)netbuf->inbuf;
    parser->start_line.length = 0;
    
    parser->offset = 0;

    return parser;
}

void http_parser_free(HttpParser *parser) {
    free(parser);
}

void http_parser_update_request(HttpParser *parser, HTTPRequest *request) {
    request->method = parser->method;
    request->uri = parser->uri;
    request->httpv = parser->httpv;
}

int http_parser_process(HttpParser *parser) {
    switch(parser->state) {
        case 0: {
            int r = get_start_line(parser);
            switch(r) {
                case 0: break;
                default: return r;
            }
            if(parser->type == 0) {
                if(parse_request_line(parser)) {
                    return 2;
                }
            } else {
                if(parse_response_line(parser)) {
                    return 2;
                }
            }
            
            parser->state++;
        }
        case 1: {
            return http_parser_parse_header(parser);
        }
        case 2: {
            return 0;
        }
    }
    return -1;
}

int http_parser_validate(HttpParser *parser) {
    if(parser->type == 0) {
        if (!parser->method.ptr || parser->method.length == 0
            || !parser->uri.ptr || parser->uri.length == 0
            || !parser->httpv.ptr || parser->httpv.length == 0)
        {
            return 0;
        }
    } else {
        if (!parser->httpv.ptr || parser->httpv.length == 0
            || !parser->msg.ptr || parser->msg.length == 0
            || parser->status == 0)
        {
            return 0;
        }
    }
    
    return 1;
}

int get_start_line(HttpParser *parser) {
    netbuf *buf = parser->netbuf;
    while(buf->pos < buf->cursize) {
        unsigned char c = buf->inbuf[buf->pos];
        if(c == '\n') {
            size_t lnlen = buf->pos - parser->offset + 1;
            if(lnlen <= 2) {
                if(lnlen == 1 || buf->inbuf[buf->pos-1] == '\r') {
                    // skip empty line
                    buf->pos++;
                    parser->offset = buf->pos;
                    return 1;
                }
                // insufficient chars for request, return error
                return 2;
            }
            if(buf->inbuf[buf->pos - 1] == '\r') {
                parser->start_line.length = lnlen - 1;
            } else {
                parser->start_line.length = lnlen;
            }
            parser->start_line.ptr = (char*)buf->inbuf + parser->offset;
            buf->pos++;
            return 0;
        }
        buf->pos++;
    }
    return 1;
}

int http_parser_parse_header(HttpParser *parser) {
    netbuf *buf = parser->netbuf;

    parser->offset = buf->pos; // line offset
    parser->name.ptr = NULL;
    parser->name.length = 0;
    parser->value.ptr = NULL;
    parser->value.length = 0;
    while(1) {
        if(buf->pos >= buf->cursize) {
            return 1;
        }
        char c = (char)buf->inbuf[buf->pos++];

        if(c > 32) {
            parser->wl = 0;
            if(c == ':' && parser->value.ptr == NULL) {
                parser->name.ptr = (char*)buf->inbuf + parser->offset;
                parser->name.length = buf->pos - parser->offset - 1;
            } else if(parser->name.ptr != NULL && parser->value.ptr == NULL) {
                parser->value.ptr = (char*)buf->inbuf + buf->pos - 1;
            }
        } else if(c == '\n') {
            if(parser->wl) {
                // line contains only white space -> end of request
                parser->state++;
                return 0;
            } else {
                parser->offset = buf->pos;
                if(parser->name.length != 0) {
                    if(parser->value.ptr) {
                        parser->value.length = (buf->inbuf + buf->pos - 1)
                            - (unsigned char*)parser->value.ptr;
                        if(buf->inbuf[buf->pos-2] == '\r') {
                            parser->value.length--;
                        }
                    } else {
                        parser->value.ptr = "";
                    }
                    // add header
                    header_array_add(
                            parser->headers,
                            parser->name,
                            parser->value);
                } else {
                    // error: no value
                    return 2;
                }
                parser->name.ptr = NULL;
                parser->value.ptr = NULL;
                parser->name.length = 0;
                parser->value.length = 0;
                parser->wl = 1;
            }
        }
    }
}

int parse_request_line(HttpParser *parser) {
    cxmutstr line = parser->start_line;
    
    // parse method, url and http version
    
    int i = 0;
    int ns = 0;

    parser->method.ptr = line.ptr;
    for(;i<line.length;i++) {
        if(!ns && line.ptr[i] == ' ') {
            ns = 1;
            parser->method.length = i;
        } else if(ns) {
            if(line.ptr[i] != ' ') {
                break;
            }
        }
    }
    if(i == line.length || parser->method.length == 0) {
        return 1;
    }

    parser->uri.ptr = line.ptr + i;
    ns = 0;
    int s = i;
    for(;i<line.length;i++) {
        if(!ns && isspace(line.ptr[i])) {
            ns = 1;
            parser->uri.length = i - s;
        } else if(ns) {
            if(line.ptr[i] > 32) {
                break;
            }
        }
    }
    if(i == line.length) {
        return 1;
    }

    parser->httpv.ptr = line.ptr + i;
    ns = 0;
    s = i;
    for(;i<line.length;i++) {
        if(!ns && isspace(line.ptr[i])) {
            ns = 1;
            parser->httpv.length = i - s;
        } else if(ns) {
            if(line.ptr[i] > 32) {
                return 1; // non-whitespace char after httpv
            }
        }
    }

    return 0;
}

int parse_response_line(HttpParser *parser) {
    cxmutstr line = parser->start_line;
    
    // parse http version, status num, status message
    
    int i = 0;
    int ns = 0;
    
    parser->httpv.ptr = line.ptr;
    for(;i<line.length;i++) {
        if(!ns && line.ptr[i] == ' ') {
            ns = 1;
            parser->httpv.length = i;
        } else if(ns) {
            if(line.ptr[i] != ' ') {
                break;
            }
        }
    }
    if(i == line.length || parser->httpv.length == 0) {
        return 1;
    }
    
    ns = 0;
    int s = i;
    cxmutstr num_str;
    num_str.ptr = line.ptr + i;
    num_str.length = 0;
    for(;i<line.length;i++) {
        if(!ns && isspace(line.ptr[i])) {
            ns = 1;
            num_str.length = i - s;
        } else if(ns) {
            if(line.ptr[i] > 32) {
                break;
            }
        }
    }
    
    if(num_str.length != 3) {
        return 1;
    }
    if(cx_strtoi(num_str, &parser->status, 10)) {
        return 1;
    }
    
    parser->msg.ptr = line.ptr + i;
    ns = 1;
    s = i;
    for(;i<line.length;i++) {
        char c = line.ptr[i];
        if(ns && c < 33) {
            parser->msg.length = i - s;
            ns = 0;
        } else {
            ns = 1;
        }
    }
    
    return parser->msg.length == 0;
}

mercurial