123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- {
- HTTPBase: Common HTTP utility declarations and classes
- Copyright (C) 2000-2003 by Sebastian Guenther ([email protected])
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- }
- unit HTTPBase;
- interface
- uses Classes, fpAsync;
- const
- fieldAccept = 'Accept';
- fieldAcceptCharset = 'Accept-Charset';
- fieldAcceptEncoding = 'Accept-Encoding';
- fieldAcceptLanguage = 'Accept-Language';
- fieldAuthorization = 'Authorization';
- fieldConnection = 'Connection';
- fieldContentEncoding = 'Content-Encoding';
- fieldContentLanguage = 'Content-Language';
- fieldContentLength = 'Content-Length';
- fieldContentType = 'Content-Type';
- fieldCookie = 'Cookie';
- fieldDate = 'Date';
- fieldExpires = 'Expires';
- fieldFrom = 'From';
- fieldIfModifiedSince = 'If-Modified-Since';
- fieldLastModified = 'Last-Modified';
- fieldLocation = 'Location';
- fieldPragma = 'Pragma';
- fieldReferer = 'Referer';
- fieldRetryAfter = 'Retry-After';
- fieldServer = 'Server';
- fieldSetCookie = 'Set-Cookie';
- fieldUserAgent = 'User-Agent';
- fieldWWWAuthenticate = 'WWW-Authenticate';
- type
- PHttpField = ^THttpField;
- THttpField = record
- Name, Value: String;
- end;
- THttpHeader = class
- protected
- FReader: TAsyncStreamLineReader;
- FWriter: TAsyncWriteStream;
- FOnCompleted: TNotifyEvent;
- FOnEOF: TNotifyEvent;
- FFields: TList;
- procedure ParseFirstHeaderLine(const line: String); virtual; abstract;
- procedure LineReceived(const ALine: String);
- procedure ReaderEOF(Sender: TObject);
- function GetFirstHeaderLine: String; virtual; abstract;
- procedure WriterCompleted(ASender: TObject);
- function GetFieldCount: Integer;
- function GetFields(AIndex: Integer): String;
- function GetFieldNames(AIndex: Integer): String;
- procedure SetFieldNames(AIndex: Integer; const AName: String);
- function GetFieldValues(AIndex: Integer): String;
- procedure SetFieldValues(AIndex: Integer; const AValue: String);
- function GetAccept: String;
- procedure SetAccept(const AValue: String);
- function GetAcceptCharset: String;
- procedure SetAcceptCharset(const AValue: String);
- function GetAcceptEncoding: String;
- procedure SetAcceptEncoding(const AValue: String);
- function GetAcceptLanguage: String;
- procedure SetAcceptLanguage(const AValue: String);
- function GetAuthorization: String;
- procedure SetAuthorization(const AValue: String);
- function GetConnection: String;
- procedure SetConnection(const AValue: String);
- function GetContentEncoding: String;
- procedure SetContentEncoding(const AValue: String);
- function GetContentLanguage: String;
- procedure SetContentLanguage(const AValue: String);
- function GetContentLength: Integer;
- procedure SetContentLength(AValue: Integer);
- function GetContentType: String;
- procedure SetContentType(const AValue: String);
- function Get_Cookie: String;
- procedure Set_Cookie(const AValue: String);
- function GetDate: String;
- procedure SetDate(const AValue: String);
- function GetExpires: String;
- procedure SetExpires(const AValue: String);
- function GetFrom: String;
- procedure SetFrom(const AValue: String);
- function GetIfModifiedSince: String;
- procedure SetIfModifiedSince(const AValue: String);
- function GetLastModified: String;
- procedure SetLastModified(const AValue: String);
- function GetLocation: String;
- procedure SetLocation(const AValue: String);
- function GetPragma: String;
- procedure SetPragma(const AValue: String);
- function GetReferer: String;
- procedure SetReferer(const AValue: String);
- function GetRetryAfter: String;
- procedure SetRetryAfter(const AValue: String);
- function GetServer: String;
- procedure SetServer(const AValue: String);
- function Get_SetCookie: String;
- procedure Set_SetCookie(const AValue: String);
- function GetUserAgent: String;
- procedure SetUserAgent(const AValue: String);
- function GetWWWAuthenticate: String;
- procedure SetWWWAuthenticate(const AValue: String);
- public
- DataReceived, CmdReceived: Boolean; // !!!: Only temporarily here
- HttpVersion: String;
- constructor Create;
- destructor Destroy; override;
- procedure SetFieldByName(const AName, AValue: String);
- function GetFieldByName(const AName: String): String;
- procedure AsyncSend(AManager: TEventLoop; AStream: THandleStream);
- procedure AsyncReceive(AManager: TEventLoop; AStream: THandleStream);
- property Reader: TAsyncStreamLineReader read FReader;
- property Writer: TAsyncWriteStream read FWriter;
- property FieldCount: Integer read GetFieldCount;
- property Fields[AIndex: Integer]: String read GetFields;
- property FieldNames[AIndex: Integer]: String read GetFieldNames write SetFieldNames;
- property FieldValues[AIndex: Integer]: String read GetFieldValues write SetFieldValues;
- property OnCompleted: TNotifyEvent read FOnCompleted write FOnCompleted;
- property OnEOF: TNotifyEvent read FOnEOF write FOnEOF;
- property Accept: String read GetAccept write SetAccept;
- property AcceptCharset: String read GetAcceptCharset write SetAcceptCharset;
- property AcceptEncoding: String read GetAcceptEncoding write SetAcceptEncoding;
- property AcceptLanguage: String read GetAcceptLanguage write SetAcceptLanguage;
- property Authorization: String read GetAuthorization write SetAuthorization;
- property Connection: String read GetConnection write SetConnection;
- property ContentEncoding: String read GetContentEncoding write SetContentEncoding;
- property ContentLanguage: String read GetContentLanguage write SetContentLanguage;
- property ContentLength: Integer read GetContentLength write SetContentLength;
- property ContentType: String read GetContentType write SetContentType;
- property Cookie: String read Get_Cookie write Set_Cookie;
- property Date: String read GetDate write SetDate;
- property Expires: String read GetExpires write SetExpires;
- property From: String read GetFrom write SetFrom;
- property IfModifiedSince: String read GetIfModifiedSince write SetIfModifiedSince;
- property LastModified: String read GetLastModified write SetLastModified;
- property Location: String read GetLocation write SetLocation;
- property Pragma: String read GetPragma write SetPragma;
- property Referer: String read GetReferer write SetReferer;
- property RetryAfter: String read GetRetryAfter write SetRetryAfter;
- property Server: String read GetServer write SetServer;
- property SetCookie: String read Get_SetCookie write Set_SetCookie;
- property UserAgent: String read GetUserAgent write SetUserAgent;
- property WWWAuthenticate: String read GetWWWAuthenticate write SetWWWAuthenticate;
- end;
- THttpRequestHeader = class(THttpHeader)
- protected
- procedure ParseFirstHeaderLine(const line: String); override;
- function GetFirstHeaderLine: String; override;
- public
- CommandLine: String;
- Command: String;
- URI: String; // Uniform Resource Identifier
- QueryString: String;
- end;
- THttpResponseHeader = class(THttpHeader)
- protected
- procedure ParseFirstHeaderLine(const line: String); override;
- function GetFirstHeaderLine: String; override;
- public
- Code: Integer;
- CodeText: String;
- constructor Create;
- end;
- implementation
- uses SysUtils;
- // THttpHeader
- procedure THttpHeader.LineReceived(const ALine: String);
- var
- i: Integer;
- begin
- if Length(ALine) = 0 then
- begin
- FReader.OnLine := nil; // Stop receiving
- FReader.StopAndFree;
- if Assigned(FOnCompleted) then
- FOnCompleted(Self);
- FReader := nil;
- end else
- DataReceived := True;
- if not CmdReceived then
- begin
- CmdReceived := True;
- ParseFirstHeaderLine(ALine);
- end else
- begin
- i := Pos(':', ALine);
- SetFieldByName(Trim(Copy(ALine, 1, i - 1)),
- Trim(Copy(ALine, i + 1, Length(ALine))));
- end;
- end;
- procedure THttpHeader.ReaderEOF(Sender: TObject);
- begin
- if Assigned(OnEOF) then
- OnEOF(Self);
- end;
- procedure THttpHeader.WriterCompleted(ASender: TObject);
- begin
- if Assigned(FOnCompleted) then
- FOnCompleted(Self);
- FreeAndNil(FWriter);
- end;
- function THttpHeader.GetFieldCount: Integer;
- begin
- Result := FFields.Count;
- end;
- function THttpHeader.GetFields(AIndex: Integer): String;
- begin
- Result := FieldNames[AIndex] + ': ' + FieldValues[AIndex];
- end;
- function THttpHeader.GetFieldNames(AIndex: Integer): String;
- begin
- Result := PHttpField(FFields.Items[AIndex])^.Name;
- end;
- procedure THttpHeader.SetFieldNames(AIndex: Integer; const AName: String);
- begin
- PHttpField(FFields.Items[AIndex])^.Name := AName;
- end;
- function THttpHeader.GetFieldValues(AIndex: Integer): String;
- begin
- Result := PHttpField(FFields.Items[AIndex])^.Value;
- end;
- procedure THttpHeader.SetFieldValues(AIndex: Integer; const AValue: String);
- begin
- PHttpField(FFields.Items[AIndex])^.Value := AValue;
- end;
- function THttpHeader.GetAccept: String; begin Result := GetFieldByName(fieldAccept) end;
- procedure THttpHeader.SetAccept(const AValue: String); begin SetFieldByName(fieldAccept, AValue) end;
- function THttpHeader.GetAcceptCharset: String; begin Result := GetFieldByName(fieldAcceptCharset) end;
- procedure THttpHeader.SetAcceptCharset(const AValue: String); begin SetFieldByName(fieldAcceptCharset, AValue) end;
- function THttpHeader.GetAcceptEncoding: String; begin Result := GetFieldByName(fieldAcceptEncoding) end;
- procedure THttpHeader.SetAcceptEncoding(const AValue: String); begin SetFieldByName(fieldAcceptEncoding, AValue) end;
- function THttpHeader.GetAcceptLanguage: String; begin Result := GetFieldByName(fieldAcceptLanguage) end;
- procedure THttpHeader.SetAcceptLanguage(const AValue: String); begin SetFieldByName(fieldAcceptLanguage, AValue) end;
- function THttpHeader.GetAuthorization: String; begin Result := GetFieldByName(fieldAuthorization) end;
- procedure THttpHeader.SetAuthorization(const AValue: String); begin SetFieldByName(fieldAuthorization, AValue) end;
- function THttpHeader.GetConnection: String; begin Result := GetFieldByName(fieldConnection) end;
- procedure THttpHeader.SetConnection(const AValue: String); begin SetFieldByName(fieldConnection, AValue) end;
- function THttpHeader.GetContentEncoding: String; begin Result := GetFieldByName(fieldContentEncoding) end;
- procedure THttpHeader.SetContentEncoding(const AValue: String); begin SetFieldByName(fieldContentEncoding, AValue) end;
- function THttpHeader.GetContentLanguage: String; begin Result := GetFieldByName(fieldContentLanguage) end;
- procedure THttpHeader.SetContentLanguage(const AValue: String); begin SetFieldByName(fieldContentLanguage, AValue) end;
- function THttpHeader.GetContentLength: Integer; var s: String; begin s := GetFieldByName(fieldContentLength); if Length(s) = 0 then Result := -1 else Result := StrToInt(s) end;
- procedure THttpHeader.SetContentLength(AValue: Integer); begin SetFieldByName(fieldContentLength, IntToStr(AValue)) end;
- function THttpHeader.GetContentType: String; begin Result := GetFieldByName(fieldContentType) end;
- procedure THttpHeader.SetContentType(const AValue: String); begin SetFieldByName(fieldContentType, AValue) end;
- function THttpHeader.Get_Cookie: String; begin Result := GetFieldByName(fieldCookie) end;
- procedure THttpHeader.Set_Cookie(const AValue: String); begin SetFieldByName(fieldCookie, AValue) end;
- function THttpHeader.GetDate: String; begin Result := GetFieldByName(fieldDate) end;
- procedure THttpHeader.SetDate(const AValue: String); begin SetFieldByName(fieldDate, AValue) end;
- function THttpHeader.GetExpires: String; begin Result := GetFieldByName(fieldExpires) end;
- procedure THttpHeader.SetExpires(const AValue: String); begin SetFieldByName(fieldExpires, AValue) end;
- function THttpHeader.GetFrom: String; begin Result := GetFieldByName(fieldFrom) end;
- procedure THttpHeader.SetFrom(const AValue: String); begin SetFieldByName(fieldFrom, AValue) end;
- function THttpHeader.GetIfModifiedSince: String; begin Result := GetFieldByName(fieldIfModifiedSince) end;
- procedure THttpHeader.SetIfModifiedSince(const AValue: String); begin SetFieldByName(fieldIfModifiedSince, AValue) end;
- function THttpHeader.GetLastModified: String; begin Result := GetFieldByName(fieldLastModified) end;
- procedure THttpHeader.SetLastModified(const AValue: String); begin SetFieldByName(fieldLastModified, AValue) end;
- function THttpHeader.GetLocation: String; begin Result := GetFieldByName(fieldLocation) end;
- procedure THttpHeader.SetLocation(const AValue: String); begin SetFieldByName(fieldLocation, AValue) end;
- function THttpHeader.GetPragma: String; begin Result := GetFieldByName(fieldPragma) end;
- procedure THttpHeader.SetPragma(const AValue: String); begin SetFieldByName(fieldPragma, AValue) end;
- function THttpHeader.GetReferer: String; begin Result := GetFieldByName(fieldReferer) end;
- procedure THttpHeader.SetReferer(const AValue: String); begin SetFieldByName(fieldReferer, AValue) end;
- function THttpHeader.GetRetryAfter: String; begin Result := GetFieldByName(fieldRetryAfter) end;
- procedure THttpHeader.SetRetryAfter(const AValue: String); begin SetFieldByName(fieldRetryAfter, AValue) end;
- function THttpHeader.GetServer: String; begin Result := GetFieldByName(fieldServer) end;
- procedure THttpHeader.SetServer(const AValue: String); begin SetFieldByName(fieldServer, AValue) end;
- function THttpHeader.Get_SetCookie: String; begin Result := GetFieldByName(fieldSetCookie) end;
- procedure THttpHeader.Set_SetCookie(const AValue: String); begin SetFieldByName(fieldSetCookie, AValue) end;
- function THttpHeader.GetUserAgent: String; begin Result := GetFieldByName(fieldUserAgent) end;
- procedure THttpHeader.SetUserAgent(const AValue: String); begin SetFieldByName(fieldUserAgent, AValue) end;
- function THttpHeader.GetWWWAuthenticate: String; begin Result := GetFieldByName(fieldWWWAuthenticate) end;
- procedure THttpHeader.SetWWWAuthenticate(const AValue: String); begin SetFieldByName(fieldWWWAuthenticate, AValue) end;
- constructor THttpHeader.Create;
- begin
- inherited Create;
- FFields := TList.Create;
- HttpVersion := '1.1';
- end;
- destructor THttpHeader.Destroy;
- var
- i: Integer;
- Field: PHttpField;
- begin
- if Assigned(FReader) then
- FReader.StopAndFree;
- if Assigned(FWriter) then
- FWriter.StopAndFree;
- for i := 0 to FFields.Count - 1 do
- begin
- Field := PHttpField(FFields.Items[i]);
- { SetLength(Field^.Name, 0);
- SetLength(Field^.Value, 0);}
- Dispose(Field);
- end;
- FFields.Free;
- inherited Destroy;
- end;
- function THttpHeader.GetFieldByName(const AName: String): String;
- var
- i: Integer;
- Name: String;
- begin
- Name := UpperCase(AName);
- for i := 0 to FFields.Count - 1 do
- if UpperCase(FieldNames[i]) = Name then
- begin
- Result := FieldValues[i];
- exit;
- end;
- SetLength(Result, 0);
- end;
- procedure THttpHeader.SetFieldByName(const AName, AValue: String);
- var
- i: Integer;
- Name: String;
- Field: PHttpField;
- begin
- Name := UpperCase(AName);
- for i := 0 to FFields.Count - 1 do
- if UpperCase(FieldNames[i]) = Name then
- begin
- FieldNames[i] := AName; // preserve case
- FieldValues[i] := AValue;
- exit;
- end;
- New(Field);
- FillChar(Field^, SizeOf(Field^), 0);
- Field^.Name := AName;
- Field^.Value := AValue;
- FFields.Add(field);
- end;
- procedure THttpHeader.AsyncSend(AManager: TEventLoop; AStream: THandleStream);
- var
- i: Integer;
- begin
- if Assigned(FWriter) then
- FWriter.StopAndFree;
- FWriter := TAsyncWriteStream.Create(AManager, AStream);
- FWriter.OnBufferSent := @WriterCompleted;
- FWriter.EndOfLineMarker := #13#10;
- FWriter.WriteLine(GetFirstHeaderLine);
- for i := 0 to FFields.Count - 1 do
- FWriter.WriteLine(Fields[i]);
- FWriter.WriteLine('');
- end;
- procedure THttpHeader.AsyncReceive(AManager: TEventLoop; AStream: THandleStream);
- begin
- CmdReceived := False;
- FReader.Free;
- FReader := TAsyncStreamLineReader.Create(AManager, AStream);
- FReader.OnLine := @LineReceived;
- FReader.OnEOF := @ReaderEOF;
- end;
- // -------------------------------------------------------------------
- // THttpRequestHeader
- // -------------------------------------------------------------------
- procedure THttpRequestHeader.ParseFirstHeaderLine(const line: String);
- var
- i: Integer;
- begin
- CommandLine := line;
- i := Pos(' ', line);
- Command := UpperCase(Copy(line, 1, i - 1));
- URI := Copy(line, i + 1, Length(line));
- // Extract HTTP version
- i := Pos(' ', URI);
- if i > 0 then
- begin
- HttpVersion := Copy(URI, i + 1, Length(URI));
- URI := Copy(URI, 1, i - 1);
- HttpVersion := Copy(HttpVersion, Pos('/', HttpVersion) + 1, Length(HttpVersion));
- end;
- // Extract query string
- i := Pos('?', URI);
- if i > 0 then
- begin
- QueryString := Copy(URI, i + 1, Length(URI));
- URI := Copy(URI, 1, i - 1);
- end;
- end;
- function THttpRequestHeader.GetFirstHeaderLine: String;
- begin
- Result := Command + ' ' + URI;
- if Length(HttpVersion) > 0 then
- Result := Result + ' HTTP/' + HttpVersion;
- end;
- // -------------------------------------------------------------------
- // THttpResponseHeader
- // -------------------------------------------------------------------
- procedure THttpResponseHeader.ParseFirstHeaderLine(const line: String);
- var
- i: Integer;
- s: String;
- begin
- i := Pos('/', line);
- s := Copy(line, i + 1, Length(line));
- i := Pos(' ', s);
- HttpVersion := Copy(s, 1, i - 1);
- s := Copy(s, i + 1, Length(s));
- i := Pos(' ', s);
- if i > 0 then begin
- CodeText := Copy(s, i + 1, Length(s));
- s := Copy(s, 1, i - 1);
- end;
- Code := StrToInt(s);
- end;
- function THttpResponseHeader.GetFirstHeaderLine: String;
- begin
- Result := Format('HTTP/%s %d %s', [HttpVersion, Code, CodeText]);
- end;
- constructor THttpResponseHeader.Create;
- begin
- inherited Create;
- Code := 200;
- CodeText := 'OK';
- end;
- end.
|