2018-09-10 13:38:55 +00:00
/*
* HTTP semantics
*
* Copyright 2000 - 2018 Willy Tarreau < w @ 1 wt . eu >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
*/
# include <ctype.h>
# include <common/config.h>
# include <common/http.h>
2018-09-10 16:04:24 +00:00
# include <common/standard.h>
2018-09-10 13:38:55 +00:00
/* It is about twice as fast on recent architectures to lookup a byte in a
* table than to perform a boolean AND or OR between two tests . Refer to
* RFC2616 / RFC5234 / RFC7230 for those chars . A token is any ASCII char that is
* neither a separator nor a CTL char . An http ver_token is any ASCII which can
* be found in an HTTP version , which includes ' H ' , ' T ' , ' P ' , ' / ' , ' . ' and any
* digit . Note : please do not overwrite values in assignment since gcc - 2.95
* will not handle them correctly . It ' s worth noting that chars 128. .255 are
* nothing , not even control chars .
*/
const unsigned char http_char_classes [ 256 ] = {
[ 0 ] = HTTP_FLG_CTL ,
[ 1 ] = HTTP_FLG_CTL ,
[ 2 ] = HTTP_FLG_CTL ,
[ 3 ] = HTTP_FLG_CTL ,
[ 4 ] = HTTP_FLG_CTL ,
[ 5 ] = HTTP_FLG_CTL ,
[ 6 ] = HTTP_FLG_CTL ,
[ 7 ] = HTTP_FLG_CTL ,
[ 8 ] = HTTP_FLG_CTL ,
[ 9 ] = HTTP_FLG_SPHT | HTTP_FLG_LWS | HTTP_FLG_SEP | HTTP_FLG_CTL ,
[ 10 ] = HTTP_FLG_CRLF | HTTP_FLG_LWS | HTTP_FLG_CTL ,
[ 11 ] = HTTP_FLG_CTL ,
[ 12 ] = HTTP_FLG_CTL ,
[ 13 ] = HTTP_FLG_CRLF | HTTP_FLG_LWS | HTTP_FLG_CTL ,
[ 14 ] = HTTP_FLG_CTL ,
[ 15 ] = HTTP_FLG_CTL ,
[ 16 ] = HTTP_FLG_CTL ,
[ 17 ] = HTTP_FLG_CTL ,
[ 18 ] = HTTP_FLG_CTL ,
[ 19 ] = HTTP_FLG_CTL ,
[ 20 ] = HTTP_FLG_CTL ,
[ 21 ] = HTTP_FLG_CTL ,
[ 22 ] = HTTP_FLG_CTL ,
[ 23 ] = HTTP_FLG_CTL ,
[ 24 ] = HTTP_FLG_CTL ,
[ 25 ] = HTTP_FLG_CTL ,
[ 26 ] = HTTP_FLG_CTL ,
[ 27 ] = HTTP_FLG_CTL ,
[ 28 ] = HTTP_FLG_CTL ,
[ 29 ] = HTTP_FLG_CTL ,
[ 30 ] = HTTP_FLG_CTL ,
[ 31 ] = HTTP_FLG_CTL ,
[ ' ' ] = HTTP_FLG_SPHT | HTTP_FLG_LWS | HTTP_FLG_SEP ,
[ ' ! ' ] = HTTP_FLG_TOK ,
[ ' " ' ] = HTTP_FLG_SEP ,
[ ' # ' ] = HTTP_FLG_TOK ,
[ ' $ ' ] = HTTP_FLG_TOK ,
[ ' % ' ] = HTTP_FLG_TOK ,
[ ' & ' ] = HTTP_FLG_TOK ,
[ 39 ] = HTTP_FLG_TOK ,
[ ' ( ' ] = HTTP_FLG_SEP ,
[ ' ) ' ] = HTTP_FLG_SEP ,
[ ' * ' ] = HTTP_FLG_TOK ,
[ ' + ' ] = HTTP_FLG_TOK ,
[ ' , ' ] = HTTP_FLG_SEP ,
[ ' - ' ] = HTTP_FLG_TOK ,
[ ' . ' ] = HTTP_FLG_TOK | HTTP_FLG_VER ,
[ ' / ' ] = HTTP_FLG_SEP | HTTP_FLG_VER ,
[ ' 0 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 1 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 2 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 3 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 4 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 5 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 6 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 7 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 8 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' 9 ' ] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG ,
[ ' : ' ] = HTTP_FLG_SEP ,
[ ' ; ' ] = HTTP_FLG_SEP ,
[ ' < ' ] = HTTP_FLG_SEP ,
[ ' = ' ] = HTTP_FLG_SEP ,
[ ' > ' ] = HTTP_FLG_SEP ,
[ ' ? ' ] = HTTP_FLG_SEP ,
[ ' @ ' ] = HTTP_FLG_SEP ,
[ ' A ' ] = HTTP_FLG_TOK ,
[ ' B ' ] = HTTP_FLG_TOK ,
[ ' C ' ] = HTTP_FLG_TOK ,
[ ' D ' ] = HTTP_FLG_TOK ,
[ ' E ' ] = HTTP_FLG_TOK ,
[ ' F ' ] = HTTP_FLG_TOK ,
[ ' G ' ] = HTTP_FLG_TOK ,
[ ' H ' ] = HTTP_FLG_TOK | HTTP_FLG_VER ,
[ ' I ' ] = HTTP_FLG_TOK ,
[ ' J ' ] = HTTP_FLG_TOK ,
[ ' K ' ] = HTTP_FLG_TOK ,
[ ' L ' ] = HTTP_FLG_TOK ,
[ ' M ' ] = HTTP_FLG_TOK ,
[ ' N ' ] = HTTP_FLG_TOK ,
[ ' O ' ] = HTTP_FLG_TOK ,
[ ' P ' ] = HTTP_FLG_TOK | HTTP_FLG_VER ,
[ ' Q ' ] = HTTP_FLG_TOK ,
[ ' R ' ] = HTTP_FLG_TOK | HTTP_FLG_VER ,
[ ' S ' ] = HTTP_FLG_TOK | HTTP_FLG_VER ,
[ ' T ' ] = HTTP_FLG_TOK | HTTP_FLG_VER ,
[ ' U ' ] = HTTP_FLG_TOK ,
[ ' V ' ] = HTTP_FLG_TOK ,
[ ' W ' ] = HTTP_FLG_TOK ,
[ ' X ' ] = HTTP_FLG_TOK ,
[ ' Y ' ] = HTTP_FLG_TOK ,
[ ' Z ' ] = HTTP_FLG_TOK ,
[ ' [ ' ] = HTTP_FLG_SEP ,
[ 92 ] = HTTP_FLG_SEP ,
[ ' ] ' ] = HTTP_FLG_SEP ,
[ ' ^ ' ] = HTTP_FLG_TOK ,
[ ' _ ' ] = HTTP_FLG_TOK ,
[ ' ` ' ] = HTTP_FLG_TOK ,
[ ' a ' ] = HTTP_FLG_TOK ,
[ ' b ' ] = HTTP_FLG_TOK ,
[ ' c ' ] = HTTP_FLG_TOK ,
[ ' d ' ] = HTTP_FLG_TOK ,
[ ' e ' ] = HTTP_FLG_TOK ,
[ ' f ' ] = HTTP_FLG_TOK ,
[ ' g ' ] = HTTP_FLG_TOK ,
[ ' h ' ] = HTTP_FLG_TOK ,
[ ' i ' ] = HTTP_FLG_TOK ,
[ ' j ' ] = HTTP_FLG_TOK ,
[ ' k ' ] = HTTP_FLG_TOK ,
[ ' l ' ] = HTTP_FLG_TOK ,
[ ' m ' ] = HTTP_FLG_TOK ,
[ ' n ' ] = HTTP_FLG_TOK ,
[ ' o ' ] = HTTP_FLG_TOK ,
[ ' p ' ] = HTTP_FLG_TOK ,
[ ' q ' ] = HTTP_FLG_TOK ,
[ ' r ' ] = HTTP_FLG_TOK ,
[ ' s ' ] = HTTP_FLG_TOK ,
[ ' t ' ] = HTTP_FLG_TOK ,
[ ' u ' ] = HTTP_FLG_TOK ,
[ ' v ' ] = HTTP_FLG_TOK ,
[ ' w ' ] = HTTP_FLG_TOK ,
[ ' x ' ] = HTTP_FLG_TOK ,
[ ' y ' ] = HTTP_FLG_TOK ,
[ ' z ' ] = HTTP_FLG_TOK ,
[ ' { ' ] = HTTP_FLG_SEP ,
[ ' | ' ] = HTTP_FLG_TOK ,
[ ' } ' ] = HTTP_FLG_SEP ,
[ ' ~ ' ] = HTTP_FLG_TOK ,
[ 127 ] = HTTP_FLG_CTL ,
} ;
2018-09-10 16:04:24 +00:00
/* We must put the messages here since GCC cannot initialize consts depending
* on strlen ( ) .
*/
struct buffer http_err_chunks [ HTTP_ERR_SIZE ] ;
const struct ist HTTP_100 = IST ( " HTTP/1.1 100 Continue \r \n \r \n " ) ;
/* Warning: no "connection" header is provided with the 3xx messages below */
const char * HTTP_301 =
" HTTP/1.1 301 Moved Permanently \r \n "
" Content-length: 0 \r \n "
" Location: " ; /* not terminated since it will be concatenated with the URL */
const char * HTTP_302 =
" HTTP/1.1 302 Found \r \n "
" Cache-Control: no-cache \r \n "
" Content-length: 0 \r \n "
" Location: " ; /* not terminated since it will be concatenated with the URL */
/* same as 302 except that the browser MUST retry with the GET method */
const char * HTTP_303 =
" HTTP/1.1 303 See Other \r \n "
" Cache-Control: no-cache \r \n "
" Content-length: 0 \r \n "
" Location: " ; /* not terminated since it will be concatenated with the URL */
/* same as 302 except that the browser MUST retry with the same method */
const char * HTTP_307 =
" HTTP/1.1 307 Temporary Redirect \r \n "
" Cache-Control: no-cache \r \n "
" Content-length: 0 \r \n "
" Location: " ; /* not terminated since it will be concatenated with the URL */
/* same as 301 except that the browser MUST retry with the same method */
const char * HTTP_308 =
" HTTP/1.1 308 Permanent Redirect \r \n "
" Content-length: 0 \r \n "
" Location: " ; /* not terminated since it will be concatenated with the URL */
/* Warning: this one is an sprintf() fmt string, with <realm> as its only argument */
const char * HTTP_401_fmt =
" HTTP/1.0 401 Unauthorized \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" WWW-Authenticate: Basic realm= \" %s \" \r \n "
" \r \n "
" <html><body><h1>401 Unauthorized</h1> \n You need a valid user and password to access this content. \n </body></html> \n " ;
const char * HTTP_407_fmt =
" HTTP/1.0 407 Unauthorized \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" Proxy-Authenticate: Basic realm= \" %s \" \r \n "
" \r \n "
" <html><body><h1>407 Unauthorized</h1> \n You need a valid user and password to access this content. \n </body></html> \n " ;
const int http_err_codes [ HTTP_ERR_SIZE ] = {
[ HTTP_ERR_200 ] = 200 , /* used by "monitor-uri" */
[ HTTP_ERR_400 ] = 400 ,
[ HTTP_ERR_403 ] = 403 ,
[ HTTP_ERR_405 ] = 405 ,
[ HTTP_ERR_408 ] = 408 ,
[ HTTP_ERR_421 ] = 421 ,
[ HTTP_ERR_425 ] = 425 ,
[ HTTP_ERR_429 ] = 429 ,
[ HTTP_ERR_500 ] = 500 ,
[ HTTP_ERR_502 ] = 502 ,
[ HTTP_ERR_503 ] = 503 ,
[ HTTP_ERR_504 ] = 504 ,
} ;
static const char * http_err_msgs [ HTTP_ERR_SIZE ] = {
[ HTTP_ERR_200 ] =
" HTTP/1.0 200 OK \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>200 OK</h1> \n Service ready. \n </body></html> \n " ,
[ HTTP_ERR_400 ] =
" HTTP/1.0 400 Bad request \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>400 Bad request</h1> \n Your browser sent an invalid request. \n </body></html> \n " ,
[ HTTP_ERR_403 ] =
" HTTP/1.0 403 Forbidden \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>403 Forbidden</h1> \n Request forbidden by administrative rules. \n </body></html> \n " ,
[ HTTP_ERR_405 ] =
" HTTP/1.0 405 Method Not Allowed \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>405 Method Not Allowed</h1> \n A request was made of a resource using a request method not supported by that resource \n </body></html> \n " ,
[ HTTP_ERR_408 ] =
" HTTP/1.0 408 Request Time-out \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>408 Request Time-out</h1> \n Your browser didn't send a complete request in time. \n </body></html> \n " ,
[ HTTP_ERR_421 ] =
" HTTP/1.0 421 Misdirected Request \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>421 Misdirected Request</h1> \n Request sent to a non-authoritative server. \n </body></html> \n " ,
[ HTTP_ERR_425 ] =
" HTTP/1.0 425 Too Early \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>425 Too Early</h1> \n Your browser sent early data. \n </body></html> \n " ,
[ HTTP_ERR_429 ] =
" HTTP/1.0 429 Too Many Requests \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>429 Too Many Requests</h1> \n You have sent too many requests in a given amount of time. \n </body></html> \n " ,
[ HTTP_ERR_500 ] =
" HTTP/1.0 500 Internal Server Error \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>500 Internal Server Error</h1> \n An internal server error occured. \n </body></html> \n " ,
[ HTTP_ERR_502 ] =
" HTTP/1.0 502 Bad Gateway \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>502 Bad Gateway</h1> \n The server returned an invalid or incomplete response. \n </body></html> \n " ,
[ HTTP_ERR_503 ] =
" HTTP/1.0 503 Service Unavailable \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>503 Service Unavailable</h1> \n No server is available to handle this request. \n </body></html> \n " ,
[ HTTP_ERR_504 ] =
" HTTP/1.0 504 Gateway Time-out \r \n "
" Cache-Control: no-cache \r \n "
" Connection: close \r \n "
" Content-Type: text/html \r \n "
" \r \n "
" <html><body><h1>504 Gateway Time-out</h1> \n The server didn't respond in time. \n </body></html> \n " ,
} ;
2018-09-10 13:38:55 +00:00
const struct ist http_known_methods [ HTTP_METH_OTHER ] = {
[ HTTP_METH_OPTIONS ] = IST ( " OPTIONS " ) ,
[ HTTP_METH_GET ] = IST ( " GET " ) ,
[ HTTP_METH_HEAD ] = IST ( " HEAD " ) ,
[ HTTP_METH_POST ] = IST ( " POST " ) ,
[ HTTP_METH_PUT ] = IST ( " PUT " ) ,
[ HTTP_METH_DELETE ] = IST ( " DELETE " ) ,
[ HTTP_METH_TRACE ] = IST ( " TRACE " ) ,
[ HTTP_METH_CONNECT ] = IST ( " CONNECT " ) ,
} ;
/*
* returns a known method among HTTP_METH_ * or HTTP_METH_OTHER for all unknown
* ones .
*/
enum http_meth_t find_http_meth ( const char * str , const int len )
{
const struct ist m = ist2 ( str , len ) ;
if ( isteq ( m , ist ( " GET " ) ) ) return HTTP_METH_GET ;
else if ( isteq ( m , ist ( " HEAD " ) ) ) return HTTP_METH_HEAD ;
else if ( isteq ( m , ist ( " POST " ) ) ) return HTTP_METH_POST ;
else if ( isteq ( m , ist ( " CONNECT " ) ) ) return HTTP_METH_CONNECT ;
else if ( isteq ( m , ist ( " PUT " ) ) ) return HTTP_METH_PUT ;
else if ( isteq ( m , ist ( " OPTIONS " ) ) ) return HTTP_METH_OPTIONS ;
else if ( isteq ( m , ist ( " DELETE " ) ) ) return HTTP_METH_DELETE ;
else if ( isteq ( m , ist ( " TRACE " ) ) ) return HTTP_METH_TRACE ;
else return HTTP_METH_OTHER ;
}
2018-09-10 15:45:34 +00:00
2018-09-10 16:04:24 +00:00
/* This function returns HTTP_ERR_<num> (enum) matching http status code.
* Returned value should match codes from http_err_codes .
*/
const int http_get_status_idx ( unsigned int status )
{
switch ( status ) {
case 200 : return HTTP_ERR_200 ;
case 400 : return HTTP_ERR_400 ;
case 403 : return HTTP_ERR_403 ;
case 405 : return HTTP_ERR_405 ;
case 408 : return HTTP_ERR_408 ;
case 421 : return HTTP_ERR_421 ;
case 425 : return HTTP_ERR_425 ;
case 429 : return HTTP_ERR_429 ;
case 500 : return HTTP_ERR_500 ;
case 502 : return HTTP_ERR_502 ;
case 503 : return HTTP_ERR_503 ;
case 504 : return HTTP_ERR_504 ;
default : return HTTP_ERR_500 ;
}
}
/* This function returns a reason associated with the HTTP status.
* This function never fails , a message is always returned .
*/
const char * http_get_reason ( unsigned int status )
{
switch ( status ) {
case 100 : return " Continue " ;
case 101 : return " Switching Protocols " ;
case 102 : return " Processing " ;
case 200 : return " OK " ;
case 201 : return " Created " ;
case 202 : return " Accepted " ;
case 203 : return " Non-Authoritative Information " ;
case 204 : return " No Content " ;
case 205 : return " Reset Content " ;
case 206 : return " Partial Content " ;
case 207 : return " Multi-Status " ;
case 210 : return " Content Different " ;
case 226 : return " IM Used " ;
case 300 : return " Multiple Choices " ;
case 301 : return " Moved Permanently " ;
case 302 : return " Moved Temporarily " ;
case 303 : return " See Other " ;
case 304 : return " Not Modified " ;
case 305 : return " Use Proxy " ;
case 307 : return " Temporary Redirect " ;
case 308 : return " Permanent Redirect " ;
case 310 : return " Too many Redirects " ;
case 400 : return " Bad Request " ;
case 401 : return " Unauthorized " ;
case 402 : return " Payment Required " ;
case 403 : return " Forbidden " ;
case 404 : return " Not Found " ;
case 405 : return " Method Not Allowed " ;
case 406 : return " Not Acceptable " ;
case 407 : return " Proxy Authentication Required " ;
case 408 : return " Request Time-out " ;
case 409 : return " Conflict " ;
case 410 : return " Gone " ;
case 411 : return " Length Required " ;
case 412 : return " Precondition Failed " ;
case 413 : return " Request Entity Too Large " ;
case 414 : return " Request-URI Too Long " ;
case 415 : return " Unsupported Media Type " ;
case 416 : return " Requested range unsatisfiable " ;
case 417 : return " Expectation failed " ;
case 418 : return " I'm a teapot " ;
case 421 : return " Misdirected Request " ;
case 422 : return " Unprocessable entity " ;
case 423 : return " Locked " ;
case 424 : return " Method failure " ;
case 425 : return " Too Early " ;
case 426 : return " Upgrade Required " ;
case 428 : return " Precondition Required " ;
case 429 : return " Too Many Requests " ;
case 431 : return " Request Header Fields Too Large " ;
case 449 : return " Retry With " ;
case 450 : return " Blocked by Windows Parental Controls " ;
case 451 : return " Unavailable For Legal Reasons " ;
case 456 : return " Unrecoverable Error " ;
case 499 : return " client has closed connection " ;
case 500 : return " Internal Server Error " ;
case 501 : return " Not Implemented " ;
case 502 : return " Bad Gateway or Proxy Error " ;
case 503 : return " Service Unavailable " ;
case 504 : return " Gateway Time-out " ;
case 505 : return " HTTP Version not supported " ;
case 506 : return " Variant also negociate " ;
case 507 : return " Insufficient storage " ;
case 508 : return " Loop detected " ;
case 509 : return " Bandwidth Limit Exceeded " ;
case 510 : return " Not extended " ;
case 511 : return " Network authentication required " ;
case 520 : return " Web server is returning an unknown error " ;
default :
switch ( status ) {
case 100 . . . 199 : return " Informational " ;
case 200 . . . 299 : return " Success " ;
case 300 . . . 399 : return " Redirection " ;
case 400 . . . 499 : return " Client Error " ;
case 500 . . . 599 : return " Server Error " ;
default : return " Other " ;
}
}
}
2018-09-10 15:45:34 +00:00
/* Parse the URI from the given transaction (which is assumed to be in request
* phase ) and look for the " / " beginning the PATH . If not found , ist2 ( 0 , 0 ) is
* returned . Otherwise the pointer and length are returned .
*/
struct ist http_get_path ( const struct ist uri )
{
const char * ptr , * end ;
if ( ! uri . len )
goto not_found ;
ptr = uri . ptr ;
end = ptr + uri . len ;
/* RFC7230, par. 2.7 :
* Request - URI = " * " | absuri | abspath | authority
*/
if ( * ptr = = ' * ' )
goto not_found ;
if ( isalpha ( ( unsigned char ) * ptr ) ) {
/* this is a scheme as described by RFC3986, par. 3.1 */
ptr + + ;
while ( ptr < end & &
( isalnum ( ( unsigned char ) * ptr ) | | * ptr = = ' + ' | | * ptr = = ' - ' | | * ptr = = ' . ' ) )
ptr + + ;
/* skip '://' */
if ( ptr = = end | | * ptr + + ! = ' : ' )
goto not_found ;
if ( ptr = = end | | * ptr + + ! = ' / ' )
goto not_found ;
if ( ptr = = end | | * ptr + + ! = ' / ' )
goto not_found ;
}
/* skip [user[:passwd]@]host[:[port]] */
while ( ptr < end & & * ptr ! = ' / ' )
ptr + + ;
if ( ptr = = end )
goto not_found ;
/* OK, we got the '/' ! */
return ist2 ( ptr , end - ptr ) ;
not_found :
return ist2 ( NULL , 0 ) ;
}
2018-09-10 16:04:24 +00:00
/* post-initializes the HTTP parts. Returns non-zero on error, with <err>
* pointing to the error message .
*/
int init_http ( char * * err )
{
int msg ;
for ( msg = 0 ; msg < HTTP_ERR_SIZE ; msg + + ) {
if ( ! http_err_msgs [ msg ] ) {
memprintf ( err , " Internal error: no message defined for HTTP return code %d " , msg ) ;
return 0 ;
}
http_err_chunks [ msg ] . area = ( char * ) http_err_msgs [ msg ] ;
http_err_chunks [ msg ] . data = strlen ( http_err_msgs [ msg ] ) ;
}
return 1 ;
}