Fri, 06 Feb 2026 14:06:04 +0100
refactor HttpParser to support parsing of Http responses
/* * 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) { 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; } } } 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 0; }