blob: 9a7336bf042f0eb3fa6c68b3ad006ba26b6110ff [file] [log] [blame]
/** @file
A brief file description
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "ink_platform.h"
#include "ink_defs.h"
#include "ink_string.h"
#include "ink_time.h"
#include "WebUtils.h"
#include "WebHttpMessage.h"
#include "TextBuffer.h"
#include "MIME.h"
#include "I_Version.h"
#include "Main.h"
/****************************************************************************
*
* WebHttpMessage.cc - classes to store information about incoming requests
* and create hdrs for outgoing requests
*
*
*
****************************************************************************/
const char *const httpStatStr[] = {
"100 Continue\r\n",
"101 Switching Protocols\r\n",
"200 OK\r\n",
"201 Created\r\n",
"202 Accepted\r\n",
"203 Non-Authorative Information\r\n",
"204 No Content\r\n",
"205 Reset Content\r\n",
"206 Partial Content\r\n",
"300 Multiple Choices\r\n",
"301 Moved Permanently\r\n",
"302 Moved Temporarily\r\n",
"303 See Other\r\n",
"304 Not Modified\r\n",
"305 Use Proxy\r\n",
"400 Bad Request\r\n",
"401 Unauthorized\r\n",
"402 Payment Required\r\n",
"403 Forbidden\r\n",
"404 Not Found\r\n",
"500 Internal Server Error\r\n",
"501 Not Implemented\r\n",
"502 Bad Gateway\r\n",
"503 Service Unvailable\r\n",
"504 Gateway Timeout\r\n",
"505 HTTP Version Not Supported\r\n"
};
const char *const httpStatCode[] = {
"100",
"101",
"200",
"201",
"202",
"203",
"204",
"205",
"206",
"300",
"301",
"302",
"303",
"304",
"305",
"400",
"401",
"402",
"403",
"404",
"500",
"501",
"502",
"503",
"504",
"505"
};
const char *const contentTypeStr[] = {
"text/plain",
"text/html",
"text/css",
"text/unknown",
"image/gif",
"image/jpeg",
"image/png",
"application/java-vm",
"application/x-javascript",
"application/x-x509-ca-cert",
"application/x-ns-proxy-autoconfig",
"application/zip"
};
// httpMessage::httpMessage()
//
httpMessage::httpMessage()
{
method = METHOD_NONE;
header = NULL;
body = NULL;
scheme = SCHEME_NONE;
file = NULL;
query = NULL;
conLen = -1;
referer = NULL;
conType_str = NULL;
authMessage = NULL;
parser = new Tokenizer(" \t\n\r");
modificationTime = -1;
modContentLength = -1;
client_request = NULL;
}
void
httpMessage::getLogInfo(const char **request)
{
*request = client_request;
}
// returns zero if everything was OK or non-zero if
// the request was malformed
int
httpMessage::addRequestLine(char *request)
{
const char *method_str;
const char *scheme_str;
const char *URI;
char *tmp;
int requestLen;
// Make a copy of the string so that we
// log it later
client_request = ats_strdup(request);
requestLen = strlen(client_request);
if (requestLen > 0 && client_request[requestLen - 1] == '\r') {
client_request[requestLen - 1] = '\0';
}
parser->Initialize(request, SHARE_TOKS);
method_str = (*parser)[0];
URI = (*parser)[1];
scheme_str = (*parser)[2];
// Check for an empty request
if (method_str == NULL) {
return 1;
}
// Determine Method
if (strcmp(method_str, "GET") == 0) {
method = METHOD_GET;
} else if (strcmp(method_str, "POST") == 0) {
method = METHOD_POST;
} else if (strcmp(method_str, "HEAD") == 0) {
method = METHOD_HEAD;
} else {
method = METHOD_NONE;
}
if (URI == NULL) {
return 1;
}
// Get the scheme
//
// We only understand HTTP/1.0
//
// If a browser asks for HTTP, we send back 1.0
// If there is no scheme, assume HTTP
// If there is another scheme, mark it unknown
//
if (scheme_str == NULL) {
scheme = SCHEME_NONE;
} else {
if (strncasecmp(scheme_str, "HTTP", 4) == 0) {
scheme = SCHEME_HTTP;
} else if (strncasecmp(scheme_str, "SHTTP", 5) == 0) {
scheme = SCHEME_SHTTP;
} else {
scheme = SCHEME_UNKNOWN;
}
}
// Now sort out the file verses query portion of the
// the request
//
// First check to see if the client sent us a full URL
if (strncmp("http://", URI, 7) == 0) {
URI = strstr(URI + 7, "/");
if (URI == NULL) {
return 1;
}
}
// Now allocate space for the document path portion
// of the URI along with the query if any
unsigned int amtToAllocate = strlen(URI) + 1;
file = new char[amtToAllocate];
ink_strlcpy(file, URI, amtToAllocate + 1);
tmp = strstr(file, "?");
if (tmp != NULL) {
// There is a form submission
*tmp = '\0';
query = tmp + 1;
// Remove trailing "\"
query[strlen(query)] = '\0';
}
return 0;
}
void
httpMessage::addHeader(char *hdr)
{
const char *authType;
const char *auth;
int len;
const char *hdrName;
const char *hdrArg1;
parser->Initialize(hdr, SHARE_TOKS);
hdrName = (*parser)[0];
hdrArg1 = (*parser)[1];
// All headers require at least tokens
if (hdrName == NULL || hdrArg1 == NULL) {
return;
}
if (strncasecmp("Content-length:", hdrName, 15) == 0) {
conLen = atoi(hdrArg1);
} else if (strncasecmp("Referer:", hdrName, 8) == 0) {
unsigned int amtToAllocate = strlen(hdrArg1);
referer = new char[amtToAllocate + 1];
ink_strlcpy(referer, hdrArg1, amtToAllocate + 1);
} else if (strncasecmp("Content-type:", hdrName, 13) == 0) {
unsigned int amtToAllocate = strlen(hdrArg1);
conType_str = new char[amtToAllocate + 1];
ink_strlcpy(conType_str, hdrArg1, amtToAllocate + 1);
} else if (strncasecmp("Authorization:", hdrName, 14) == 0) {
authType = hdrArg1;
if (strcmp(authType, "Basic") == 0) {
auth = (*parser)[2];
len = strlen(auth) + 1;
authMessage = new char[len];
ink_strlcpy(authMessage, auth, len);
}
} else if (strncasecmp("If-Modified-Since:", hdrName, 18) == 0) {
// Disabled due to thread safety issues
getModDate();
}
}
// httpMessage::getModDate()
//
// A function to extract info from the If-Modified-Since http
// header.
//
// Currently brokens since both strptime and mktime are not thread
// safe
//
void
httpMessage::getModDate()
{
// Since the dates have spaces in them, we have to recontruct
// them. Sigh.
int i = 1;
int numDateFields;
int dateSize = 0;
char *dateStr;
const char *clStr;
int tmpLen;
Tokenizer *equalTok = NULL;
// First, figure out the number of fields
for (i = 1; (*parser)[i] != NULL && strstr((*parser)[i++], ";") == NULL;);
numDateFields = i - 1;
if (numDateFields > 0) {
// Next, figure out the size of the string we need
for (i = 0; i < numDateFields; i++) {
dateSize += strlen((*parser)[i + 1]);
}
dateStr = new char[dateSize + 1 + numDateFields];
*dateStr = '\0';
// Rebuild the date string from the parsed
// stuff
for (i = 0; i < numDateFields; i++) {
ink_strlcat(dateStr, (*parser)[i + 1], dateSize + 1);
tmpLen = strlen(dateStr);
dateStr[tmpLen] = ' ';
dateStr[tmpLen + 1] = '\0';
}
// There could be junk of the end of array like a ;
tmpLen = strlen(dateStr);
while (!isalpha(dateStr[tmpLen])) {
dateStr[tmpLen] = '\0';
tmpLen--;
}
modificationTime = mime_parse_date(dateStr);
delete[]dateStr;
// Now figure out the content length from if modified
if (parser->getNumber() > numDateFields + 1) {
clStr = (*parser)[numDateFields + 1];
equalTok = new Tokenizer("=\r\n");
equalTok->Initialize(clStr);
if (strcasecmp("length", (*equalTok)[0]) == 0) {
modContentLength = atoi((*equalTok)[1]);
}
delete equalTok;
}
}
}
// httpMessage::addRequestBody(int fd)
//
// Read the request body of of the socket and make a local
// copy of the entire thing. Return zero if all went ok
// or return -1 with there was an error
int
httpMessage::addRequestBody(SocketInfo socketD)
{
char *nextRead;
int bytesRead = 0;
int readResult;
if (conLen < 0) {
return 0;
}
body = new char[conLen + 1];
nextRead = body;
while (bytesRead < conLen) {
readResult = socket_read(socketD, nextRead, conLen - bytesRead);
if (readResult <= 0) {
// There was an error on the read.
*nextRead = '\0';
return -1;
} else {
bytesRead += readResult;
nextRead += readResult;
}
}
*nextRead = '\0';
return 0;
}
httpMessage::~httpMessage()
{
delete[]body;
delete[]file;
delete[]referer;
delete[]conType_str;
delete[]authMessage;
ats_free(client_request);
delete parser;
}
/* 01/14/99 elam -
* Commented out because g++ is not happy with iostream.h
void httpMessage::Print() {
cout << "Method: " << method << endl;
cout << "File: " << file << endl;
cout << "Query: " << query << endl;
cout << "Scheme: " << scheme << endl;
cout << "Content Length: " << conLen << endl;
cout << "First Header: " << header << endl;
cout << "Message Body: " << body << endl << endl;
}
*/
httpResponse::httpResponse()
{
// Default is no refresh header;
refresh = -1;
conLen = -1;
conType = TEXT_HTML;
explicitConType = NULL;
refreshURL = NULL;
lastMod = -1;
authRealm = NULL;
dateResponse = NULL;
status = STATUS_INTERNAL_SERVER_ERROR;
locationURL = NULL;
cachable = 1;
}
httpResponse::~httpResponse()
{
ats_free(explicitConType);
ats_free(authRealm);
ats_free(refreshURL);
ats_free(locationURL);
ats_free(dateResponse);
}
int
httpResponse::writeHdr(SocketInfo socketD)
{
const char versionStr[] = "HTTP/1.0 ";
const char serverStr[] = "Server: ";
const char managerStr[] = "Traffic Manager ";
const char noStoreStr[] = "Cache-Control: no-store\r\n";
const char noCacheStr[] = "Pragma: no-cache\r\n";
const char lenStr[] = "Content-length: ";
const char refreshStr[] = "Refresh: ";
const char authStr[] = "WWW-Authenticate: Basic realm=\"";
const char dateStr[] = "Date: ";
const char lastModStr[] = "Last-modified: ";
const char locationStr[] = "Location: ";
const char refreshURLStr[] = "; URL=";
time_t currentTime;
const int bufSize = 512;
char buffer[bufSize];
char *reply;
int bytesWritten;
textBuffer hdr(4096);
hdr.copyFrom(versionStr, strlen(versionStr));
// Record the status
hdr.copyFrom(httpStatStr[status], strlen(httpStatStr[status]));
// Record Server Name
hdr.copyFrom(serverStr, strlen(serverStr));
hdr.copyFrom(managerStr, strlen(managerStr));
hdr.copyFrom(appVersionInfo.VersionStr, strlen(appVersionInfo.VersionStr));
hdr.copyFrom("\r\n", 2);
// Record refresh
if (refresh >= 0) {
hdr.copyFrom(refreshStr, strlen(refreshStr));
snprintf(buffer, bufSize - 1, "%d", refresh);
hdr.copyFrom(buffer, strlen(buffer));
if (refreshURL != NULL) {
hdr.copyFrom(refreshURLStr, strlen(refreshURLStr));
hdr.copyFrom(refreshURL, strlen(refreshURL));
}
hdr.copyFrom("\r\n", 2);
}
// Location Header
if (locationURL != NULL) {
hdr.copyFrom(locationStr, strlen(locationStr));
hdr.copyFrom(locationURL, strlen(locationURL));
hdr.copyFrom("\r\n", 2);
}
// Always send the current time
currentTime = time(NULL);
mime_format_date(buffer, currentTime);
// We were able to genarate the date string
hdr.copyFrom(dateStr, strlen(dateStr));
hdr.copyFrom(buffer, strlen(buffer));
hdr.copyFrom("\r\n", 2);
dateResponse = ats_strdup(buffer);
// Not cachable if marked not cachable, or has no L-M date
if ((getCachable() == 0) || (lastMod == -1)) {
// Use "Control-Control: no-store" for HTTP 1.1
// compliant browsers.
hdr.copyFrom(noStoreStr, strlen(noStoreStr));
// For non-1.1 compliant browsers, we'll set
// "Pragma: no-cache" just to be safe
hdr.copyFrom(noCacheStr, strlen(noCacheStr));
} else if (lastMod != -1) {
// Send the last modified time if we have it
mime_format_date(buffer, lastMod);
// We were able to genarate the date string
hdr.copyFrom(lastModStr, strlen(lastModStr));
hdr.copyFrom(buffer, strlen(buffer));
hdr.copyFrom("\r\n", 2);
}
snprintf(buffer, sizeof(buffer), "Content-type: %s\r\n", contentTypeStr[conType]);
hdr.copyFrom(buffer, strlen(buffer));
// Issue an authentication challenge if we are
// authorized
if (status == STATUS_UNAUTHORIZED) {
hdr.copyFrom(authStr, strlen(authStr));
hdr.copyFrom(authRealm, strlen(authRealm));
hdr.copyFrom("\"\r\n", 3);
}
if (conLen >= 0) {
hdr.copyFrom(lenStr, strlen(lenStr));
snprintf(buffer, sizeof(buffer), "%d", conLen);
hdr.copyFrom(buffer, strlen(buffer));
hdr.copyFrom("\r\n", 2);
}
// End of Header marked by empty line
hdr.copyFrom("\r\n", 2);
reply = hdr.bufPtr();
bytesWritten = socket_write(socketD, reply, strlen(reply));
return bytesWritten;
}
void
httpResponse::setContentType(const char *str)
{
if (str != NULL) {
explicitConType = ats_strdup(str);
}
}
void
httpResponse::setRealm(const char *realm)
{
if (realm != NULL) {
authRealm = ats_strdup(realm);
}
}
void
httpResponse::setRefreshURL(const char *url)
{
if (url != NULL) {
refreshURL = ats_strdup(url);
}
}
void
httpResponse::setLocationURL(const char *url)
{
if (url != NULL) {
locationURL = ats_strdup(url);
}
}
void
httpResponse::getLogInfo(const char **date, HttpStatus_t * statusIn, int *length)
{
*date = dateResponse;
*statusIn = status;
*length = conLen;
}