📄 http.c
字号:
/*******************************************************************************
**
** http.c
**
** This file is part of the ABYSS Web server project.
**
** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
** 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.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "abyss.h"
/*********************************************************************
** Request Parser
*********************************************************************/
void NextToken(char **p)
{
while (1)
switch (**p)
{
case '\t':
case ' ':
(*p)++;
break;
default:
return;
};
}
char *GetToken(char **p)
{
char *p0=*p;
while (1)
switch (**p)
{
case '\t':
case ' ':
case CR:
case LF:
case '\0':
if (p0==*p)
return NULL;
if (**p)
{
**p='\0';
(*p)++;
};
return p0;
default:
(*p)++;
};
}
char *GetMultiLine(char **p)
{
char *p0=*p;
while (1)
switch (**p)
{
case '\0':
return p0;
case '\t':
case CR:
**p=' ';
(*p)++;
break;
case ' ':
(*p)++;
break;
case LF:
**p=' ';
(*p)++;
if ((**p==' ')||(**p=='\t'))
break;
else
{
char *t=*p-1;
while (*t==' ')
*(t--)='\0';
return p0;
};
default:
(*p)++;
};
}
abyss_bool Eol(char *p)
{
NextToken(&p);
if (*p==LF)
return TRUE;
if (*p==CR)
if (*(p+1)==LF)
return TRUE;
return FALSE;
}
void NextLine(char **p)
{
while (**p)
if (**p!=LF)
(*p)++;
else
{
(*p)++;
break;
};
}
/*********************************************************************
** Request
*********************************************************************/
void RequestInit(TSession *r,TConn *c)
{
time_t t;
time(&t);
r->date=*gmtime(&t);
r->keepalive=r->cankeepalive=FALSE;
r->query=NULL;
r->host=NULL;
r->from=NULL;
r->useragent=NULL;
r->referer=NULL;
r->user=NULL;
r->port=80;
r->versionmajor=0;
r->versionminor=9;
r->server=c->server;
r->conn=c;
r->done=FALSE;
r->chunkedwrite=r->chunkedwritemode=FALSE;
r->requestline=NULL;
ListInit(&r->cookies);
ListInit(&r->ranges);
TableInit(&r->request_headers);
TableInit(&r->response_headers);
r->status=0;
StringAlloc(&(r->header));
}
void RequestFree(TSession *r)
{
if (r->requestline)
free(r->requestline);
if (r->user)
free(r->user);
ListFree(&r->cookies);
ListFree(&r->ranges);
TableFree(&r->request_headers);
TableFree(&r->response_headers);
StringFree(&(r->header));
}
abyss_bool RequestRead(TSession *r)
{
char *n,*t,*p;
uint32 vmin,vmaj;
abyss_bool ret,singleline=FALSE;
/* Ignore CRLFs in the beginning of the request (RFC2068-P30) */
do
{
if (!ConnReadLine(r->conn,&p,r->server->timeout))
{
/* Request Timeout */
ResponseStatus(r,408);
return FALSE;
};
} while (*p=='\0');
/* Request Line */
/* Jump over spaces */
NextToken(&p);
r->requestline=strdup(p);
if (!(t=GetToken(&p)))
{
/* Bad request */
ResponseStatus(r,400);
return FALSE;
};
if (strcmp(t,"GET")==0)
r->method=m_get;
else if (strcmp(p,"PUT")==0)
r->method=m_put;
else if (strcmp(t,"OPTIONS")==0)
r->method=m_options;
else if (strcmp(p,"DELETE")==0)
r->method=m_delete;
else if (strcmp(t,"POST")==0)
r->method=m_post;
else if (strcmp(p,"TRACE")==0)
r->method=m_trace;
else if (strcmp(t,"HEAD")==0)
r->method=m_head;
else
r->method=m_unknown;
/* URI and Query Decoding */
NextToken(&p);
if (!(t=GetToken(&p)))
return FALSE;
r->uri=t;
RequestUnescapeURI(r);
if (t=strchr(t,'?'))
{
*t='\0';
r->query=t+1;
};
NextToken(&p);
/* HTTP Version Decoding */
/* If no HTTP version then this is a single line request */
if (t=GetToken(&p))
{
if (sscanf(t,"HTTP/%d.%d",&vmaj,&vmin)!=2)
{
/* Bad request */
ResponseStatus(r,400);
return FALSE;
};
r->versionmajor=vmaj;
r->versionminor=vmin;
}
else
/* If it is a single line request then we can leave */
return TRUE;
/* Headers decoding */
ret= TRUE;
for (;;)
{
if (!ConnReadLine(r->conn,&p,r->server->timeout))
{
/* Request Timeout */
ResponseStatus(r,408);
return FALSE;
};
/* We have reached the empty line so all the request was read */
if (!*p)
return TRUE;
NextToken(&p);
if (!(n=GetToken(&p)))
{
/* Bad Request */
ResponseStatus(r,400);
return FALSE;
};
/* Valid Field name ? */
if (n[strlen(n)-1]!=':')
{
/* Bad Request */
ResponseStatus(r,400);
return FALSE;
};
n[strlen(n)-1]='\0';
NextToken(&p);
t=n;
while (*t)
{
*t=tolower(*t);
t++;
};
t=p;
TableAdd(&r->request_headers,n,t);
if (strcmp(n,"connection")==0)
{
/* must handle the jigsaw TE,keepalive */
if (strcasecmp(t,"keep-alive")==0)
r->keepalive=TRUE;
else
r->keepalive=FALSE;
}
else if (strcmp(n,"host")==0)
r->host=t;
else if (strcmp(n,"from")==0)
r->from=t;
else if (strcmp(n,"user-agent")==0)
r->useragent=t;
else if (strcmp(n,"referer")==0)
r->referer=t;
else if (strcmp(n,"range")==0)
{
if (strncmp(t,"bytes=",6)==0)
if (!ListAddFromString(&(r->ranges),t+6))
{
/* Bad Request */
ResponseStatus(r,400);
return FALSE;
};
}
else if (strcmp(n,"cookies")==0)
{
if (!ListAddFromString(&(r->cookies),t))
{
/* Bad Request */
ResponseStatus(r,400);
return FALSE;
};
};
};
}
char *RequestHeaderValue(TSession *r,char *name)
{
return (TableFind(&r->request_headers,name));
}
abyss_bool RequestUnescapeURI(TSession *r)
{
char *x,*y,c,d;
x=y=r->uri;
while (1)
switch (*x)
{
case '\0':
*y='\0';
return TRUE;
case '%':
x++;
c=tolower(*x++);
if ((c>='0') && (c<='9'))
c-='0';
else if ((c>='a') && (c<='f'))
c-='a'-10;
else
return FALSE;
d=tolower(*x++);
if ((d>='0') && (d<='9'))
d-='0';
else if ((d>='a') && (d<='f'))
d-='a'-10;
else
return FALSE;
*y++=((c << 4) | d);
break;
default:
*y++=*x++;
break;
};
};
abyss_bool RequestValidURI(TSession *r)
{
char *p;
if (!r->uri)
return FALSE;
if (*(r->uri)!='/')
if (strncmp(r->uri,"http://",7)!=0)
return FALSE;
else
{
r->uri+=7;
r->host=r->uri;
p=strchr(r->uri,'/');
if (!p)
{
r->uri="*";
return TRUE;
};
r->uri=p;
p=r->host;
while (*p!='/')
{
*(p-1)=*p;
p++;
};
p--;
*p='\0';
r->host--;
};
/* Host and Port Decoding */
if (r->host)
if (p=strchr(r->host,':'))
{
uint32 port=0;
*p='\0';
p++;
while (isdigit(*p) && (port<65535))
{
port=port*10+(*p)-'0';
p++;
};
r->port=port;
if (*p || port==0)
return FALSE;
};
if (strcmp(r->uri,"*")==0)
return (r->method!=m_options);
if (strchr(r->uri,'*'))
return FALSE;
return TRUE;
}
abyss_bool RequestValidURIPath(TSession *r)
{
uint32 i=0;
char *p=r->uri;
if (*p=='/')
{
i=1;
while (*p)
if (*(p++)=='/')
{
if (*p=='/')
break;
else if ((strncmp(p,"./",2)==0) || (strcmp(p,".")==0))
p++;
else if ((strncmp(p,"../",2)==0) || (strcmp(p,"..")==0))
{
p+=2;
i--;
if (i==0)
break;
}
/* Prevent accessing hidden files (starting with .) */
else if (*p=='.')
return FALSE;
else
if (*p)
i++;
};
};
return ((*p==0) && (i>0));
}
abyss_bool RequestAuth(TSession *r,char *credential,char *user,char *pass)
{
char *p,*x;
char z[80],t[80];
if (p=RequestHeaderValue(r,"authorization"))
{
NextToken(&p);
if (x=GetToken(&p))
if (strcasecmp(x,"basic")==0)
{
NextToken(&p);
sprintf(z,"%s:%s",user,pass);
Base64Encode(z,t);
if (strcmp(p,t)==0)
{
r->user=strdup(user);
return TRUE;
};
};
};
sprintf(z,"Basic realm=\"%s\"",credential);
ResponseAddField(r,"WWW-Authenticate",z);
ResponseStatus(r,401);
return FALSE;
}
/*********************************************************************
** Range
*********************************************************************/
abyss_bool RangeDecode(char *str,uint64 filesize,uint64 *start,uint64 *end)
{
char *ss;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -