Sign in Join
Wiki copytemporary

fcl-web: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: fcl-web

OldNewLine
1+## What is fpWeb
2+
3+fpWeb can be used to build web server applications. A web server application can be one of the following
4+
5+- CGI application
6+- FastCGI application
7+- Apache module
8+- A standalone HTTP(s) server using Free Pascal HTTP server components.
9+- A standalone HTTP(s) server using GNU libmicrohttp
10+- A windows SysHTTP application.
11+
12+In all cases, the development model is the same: you register handlers for a set of URLS (called routes) that your application will support.
13+
14+Then you embed these routes in one of the above environments: This means you choose the application object that you will be using.
15+
16+The lazarus IDE you have the choice of applications when you start a new project. It's possible to change the application type once the application was started, this usually just means changing the name of a unit in the project's uses clause.
17+
18+It is possible to support multiple environments within a single binary, although this takes some extra work.
19+
20+fpWeb also offers some out-of-the box support for a database REST server or for JSON RPC 2.0 mechanisms. These are built on top of the above structure. The Brook framework is also built on top of the basic architecture outlined above.
21+
22+More about this can be found in the [[fpWeb Tutorial]]
23+
24+The [[simpleserver]] example is a small stand alone server application that showcases many of the possibilities of fcl-web.
25+
26+## Using fpWeb together with Lazarus
27+
28+### Installing the weblaz fpWeb Lazarus Package
29+
30+The first step to do is installing the package which comes in the path lazarus/components/fpweb/weblaz.lpk. As usual with design-time packages, you'll have to rebuild Lazarus.
31+
32+### Creating a CGI application
33+
34+After the weblaz package is installed, a very simple CGI web application which displays an HTML page can be created by going to the Lazarus menu "File-\>New...". From the list of possible applications select "CGI Application" as in the image below, which will create a main CGI project file and a fpweb web module.
35+
36+![](/wiki-images/e/ec/New_cgi.PNG)
37+
38+The TFPWebModule allows you to manipulate properties and events using the Object Inspector.
39+
40+To add code to show the page, a request handler should be added. To do this, double click the OnRequest property in the object inspector, as in the image below:
41+
42+![](/wiki-images/3/3d/Webmodule.PNG)
43+
44+In the event handler one should write the HTML code which will be displayed by the browser. To avoid mixing Pascal and HTML, this page can be loaded from the directory the CGI executable is in by using AResponse.Contents.LoadFromFile().
45+
46+The type of the response should be set in AResponse.ContentType. For HTML pages this should have the value 'text/html;charset=utf-8'.
47+
48+Finally, Handled should be set to True to indicate that the request was successfully handled (and return a 200 OK status code to the browser). After adding this code, the web module should look like this:
49+
50+``` pascal
51+unit mainpage;
52+
53+{$mode objfpc}{$H+}
54+
55+interface
56+
57+uses
58+ Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb;
59+
60+type
61+
62+ { TFPWebModule1 }
63+
64+ TFPWebModule1 = class(TFPWebModule)
65+ procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;
66+ AResponse: TResponse; var Handled: Boolean);
67+ private
68+ { private declarations }
69+ public
70+ { public declarations }
71+ end;
72+
73+var
74+ FPWebModule1: TFPWebModule1;
75+
76+implementation
77+
78+{$R *.lfm}
79+
80+{ TFPWebModule1 }
81+
82+procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
83+ AResponse: TResponse; var Handled: Boolean);
84+begin
85+ AResponse.ContentType := 'text/html;charset=utf-8';
86+ AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
87+ Handled := True;
88+end;
89+
90+begin
91+ RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
92+end.
93+```
94+
95+### Deploying the CGI application
96+
97+This section assumes the Apache web server is used. Of course, other web servers that support CGI (nginx, cherokee) can be used, too.
98+
99+Apache can be downloaded here: <http://httpd.apache.org/download.cgi> or installed using your distribution's package manager.
100+
101+The default installation of Apache will treat all files located in its cgi-bin directory as CGI programs, so the user won't be able to access plain HTML files placed there. This directory can be set in the file httpd.conf in the following section:
102+
103+ #
104+ # ScriptAlias: This controls which directories contain server scripts.
105+ # ScriptAliases are essentially the same as Aliases, except that
106+ # documents in the target directory are treated as applications and
107+ # run by the server when requested rather than as documents sent to the
108+ # client. The same rules about trailing "/" apply to ScriptAlias
109+ # directives as to Alias.
110+ #
111+ ScriptAlias /cgi-bin/ "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/"
112+
113+If you place an executable called "mywebpage.cgi" in this directory, then the page can be accessed as <http://localhost/cgi-bin/mywebpage.cgi> (or remotely with the corresponding IP address or domain name).
114+
115+fcl-web with Lazarus on Windows produces .exe files. For Apache to serve these files you have to add this:
116+
117+ AddHandler cgi-script .exe
118+
119+And to serve your executables from another directory, you add this:
120+
121+``` xml
122+ ScriptAlias /bin/ "C:/lazarus_projects/test-fclweb/bin/"
123+ <Directory "C:/lazarus_projects/test-fclweb/bin/">
124+ AllowOverride None
125+ Options None
126+ Order allow,deny
127+ Allow from all
128+ </Directory>
129+```
130+
131+### Formatting the HTML and Reading Query fields
132+
133+The previous example just showed a plain HTML page. One might wish to e.g.
134+
135+- dynamically change the HTML page output, and
136+- as read the variables the browser passed to the webpage in Query fields (in e.g. HTTP GET and HTTP POST actions).
137+
138+A simple solution for the first problem is simply using the standard Pascal routine Format and adding %s or %d in the HTML file in the appropriate places which will receive a custom value.
139+
140+### Reading GET data
141+
142+To read the GET variables one can use ARequest.QueryFields, which is a TStrings descendent. Each variable will be in a separate line in the TStrings in the format variablename=value, similarly to how they are shown in the browser page address. To search for a specific variable one can use ARequest.QueryFields.Values\[\], passing the variable name in the brackets to receive its value back, instead of parsing the string manually.
143+
144+The resulting code:
145+
146+``` pascal
147+procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
148+ AResponse: TResponse; var Handled: Boolean);
149+var
150+ HexText, AsciiText: string;
151+begin
152+ HexText := ARequest.QueryFields.Values['hex'];
153+ AsciiText := HexToAnsii(HexText);
154+
155+ AResponse.ContentType := 'text/html;charset=utf-8';
156+ AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
157+ AResponse.Contents.Text := Format(AResponse.Contents.Text,
158+ [HexText, AsciiText]);
159+ Handled := True;
160+end;
161+```
162+
163+### Reading POST data
164+
165+Data submitted by POST requests can be obtained from `TRequest.Content`. It will come without the request headers - in other words it contains the body of the request.
166+
167+Submitted form data can also be accessed using `TRequest.ContentFields` which is the content parsed as fields separated by & and decoded. For example, the following set of form values:
168+
169+`login: dfg 345&&`\
170+`login_senha: ====`\
171+`email: dfg`
172+
173+Will be encoded in 'APPLICATION/X-WWW-FORM-URLENCODED' like this (see [wikipedia POST (HTTP)](http://en.wikipedia.org/wiki/POST_%28HTTP%29)):
174+
175+`login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg`
176+
177+And will be available in TRequest.ContentFields via the line index or using the Values property, which is more convenient:
178+
179+`TRequest.ContentFields[0]: login=dfg 345&&`\
180+`TRequest.ContentFields[1]: login_senha=====`\
181+`TRequest.ContentFields[2]: email=dfg`\
182+`TRequest.ContentFields.Values['email']: dfg`
183+
184+If using other mime-types than 'MULTIPART/FORM-DATA' and 'APPLICATION/X-WWW-FORM-URLENCODED' (the types supported by HTML forms):
185+
186+- content is only available in TRequest.Content, not TRequest.ContentFields
187+- use of these mime-types raises an exception for FPC 2.4.2 (fixed in FPC 2.5+).
188+
189+Note that HTTP POST can also send Query fields (in the URL), e.g. <http://server/bla?question=how>, and those are accessed by TRequest.QueryFields as explained in the previous section.
190+
191+``` pascal
192+procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
193+ AResponse: TResponse; var Handled: Boolean);
194+var
195+ lData: String;
196+begin
197+ lData := ARequest.Content;
198+```
199+
200+### Reading POST binary data
201+
202+Uploaded files are saved to a temporary location with a temporary filename, to retain the file you should move it to a permanent location.
203+
204+To receive data on the server that has been POSTed e.g. as multipart/form-data, use something like this:
205+
206+``` pascal
207+procedure TMainWebModule.TFPWebActions2Request(Sender: TObject;
208+ ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
209+var
210+ i: Integer;
211+begin
212+ // Process all received files
213+ for i := 0 to ARequest.Files.Count - 1 do
214+ begin
215+ // Doing something else than writeln is highly recommended ;)
216+ writeln ('Received Filename: '+ARequest.Files[i].LocalFileName);
217+ end;
218+
219+ Handled := true;
220+end;
221+```
222+
223+The client can send data using e.g. FileFormPost.
224+
225+## Specialized modules
226+
227+The *TFPWebModule* class (used below) is a simple example of a WEb module that can be used for all kinds of HTTP requests.
228+
229+However, fpweb comes with some specialized modules, that have extra support for specialized tasks:
230+
231+- The **TSimpleFileModule** class in unit [[fpwebfile]] can be used to send files. You point it to a directory, and it does the rest.
232+- The **TFPHTMLModule** class in unit *fphtml.pp* can be used to produce HTML.
233+- The **TProxyWebModule** class in unit *fpwebproxy.pp* is a ready-made forwarding proxy.
234+- The **TFPWebProviderDataModule** class in unit *fpwebdata.pp* serves data in JSON format that can be consumed by ExtJS stores.
235+- The **TSQLDBRestModule** class in unit *sqldbrestmodule.pp* implements a full REST server backed by SQLDB. See more info in [[SQLDBRestBridge]].
236+- The **TJSONRPCModule** class in unit *webjsonrpc.pp* implements a JSON-RPC service.
237+- The **TExtDirectModule** class in unit *fpextdirect.pp* implements a Ext.Direct variant of a JSON-RPC service.
238+
239+## Using multiple modules
240+
241+If there is only one module in the web application, all requests will be directed to this module.
242+
243+As your web application grows, multiple modules can be used. A new module can be added by choosing 'File - New' and then one of 'Web module' or 'HTML Web Module'.
244+
245+FCL-web uses the URL to determine how a HTTP request should be handled. It must therefore know which web-modules exist in the application. To achieve this, each module must be registered.
246+
247+Each module is registered with fcl-web in the initialization section of the unit it is defined in:
248+
249+``` pascal
250+RegisterHTTPModule('location', TMyModule);
251+```
252+
253+The module will then be invoked if an URL of the form
254+
255+`http://www.mysite.org/mycgi.cgi/location`
256+
257+or
258+
259+`http://www.mysite.org/mycgi.cgi?module=location`
260+
261+is used.
262+
263+If multiple modules are present, the name of the module must appear in the URL, or an error will be raised.
264+
265+This behaviour can also be forced for applications that have only a single module by setting the Application's property AllowDefaultModule to false:
266+
267+``` pascal
268+Application.AllowDefaultModule := False;
269+```
270+
271+In that case, the fcl-web application will always require the name of the module in the URL.
272+
273+The name of the request variable that determines the module name (by default, this is 'module') can be set in the Application.ModuleVariable property. The following code
274+
275+``` pascal
276+Application.ModuleVariable := 'm';
277+```
278+
279+ensures that the following URL is directed to TMyModule:
280+
281+`http://www.mysite.org/mycgi.cgi?m=location`
282+
283+If all this is not enough to determine the module to which the request should be passed, the Application.OnGetModule event can be used. It is of type TGetModuleEvent:
284+
285+``` pascal
286+type
287+ TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
288+ Var ModuleClass : TCustomHTTPModuleClass) of object;
289+```
290+
291+Creating an event handler for this event allows fine control over the module that is created to handle the request: the request (passed in ARequest) can be examined, and the 'ModuleClass' variable must be set to the class of the module that should handle the request.
292+
293+If 'ModuleClass' is 'Nil' on return, an error will be sent to the browser.
294+
295+## Using Actions
296+
297+A module can be used to group certain kinds of actions that logically belong together. Imagine a module TUserModule that is used to handle user management in the webpages. There can be multiple actions associated with a user:
298+
299+- Creating
300+- Deleting
301+- Editing
302+- Displaying
303+
304+These different actions can be handled by the same module. One can determine the action from the URL manually, as in:
305+
306+`http://mysite/mycgi.cgi/user?action=delete&id=12`
307+
308+This can be automated.
309+
310+In order to make it easier to distinguish between various actions, the module has a property actions: this is a collection, in which each item is associated with a different response to the request. The actions have various properties:
311+
312+Name
313+The name of the action. The URL will be examined to determine the name of the action.
314+
315+Content
316+A string. If set, this is sent to the browser.
317+
318+Contents
319+A stringlist. If set, this is sent to the browser.
320+
321+ContentProducer
322+If set, the contentproducer will handle the request.
323+
324+Default
325+if set to 'True', then this action is the default action. That means that if FCL-Web cannot determine the action name, this action will be executed.
326+
327+Template
328+If set, this template will be processed, and the results sent to the browser.
329+
330+There are also some events:
331+
332+BeforeRequest
333+executed before the request is processed. Can be used to set the 'Content' or other properties.
334+
335+AfterRequest
336+executed after the request is processed.
337+
338+OnRequest
339+an event handler to handle the request. If set, the handler is used to handle the request.
340+
341+Again, as in the case of multiple modules, the URL is used to determine which action to execute. The part right after the module part ("user" in this example) is used:
342+
343+`http://mysite/mycgi.cgi/user/delete&id=12`
344+
345+would execute the action named 'delete'.
346+
347+The 'ActionVar' property of the module can be used to set the name of the request variable to use. Setting
348+
349+``` pascal
350+UserModule.ActionVar := 'a';
351+```
352+
353+may be used to change the above URL to
354+
355+`http://mysite/mycgi.cgi/user?a=delete&id=12`
356+
357+If there is only one module in the application, the URL can be shortened to
358+
359+`http://mysite/mycgi.cgi/delete&id=12`
360+
361+## Using HTML Templates
362+
363+For information about using templates, template tags and template tag parameters to generate response pages, please refer to the fptemplate.txt file under your FPC directory in /packages/fcl-base/texts/.
364+
365+Example projects that demonstrate using templates can be found under your FPC directory in /packages/fcl-web/examples/fptemplate/ (see the README.txt in there for more). (In earlier versions, these examples were in the Lazarus directory in /components/fpweb/demo/fptemplate/)
366+
367+## Tips & troubleshooting
368+
369+### Sending result codes
370+
371+To send a different HTTP response than 200 OK, use **AResponse.Code** and **AResponse.CodeText**, e.g.
372+
373+``` pascal
374+AResponse.Code := 404;
375+AResponse.CodeText := 'Document not found';
376+```
377+
378+As of FPC 3.3.1 you can use the **SetStatus** method:
379+
380+``` pascal
381+AResponse.SetStatus(404);
382+```
383+
384+it will use the *GetHTTPStatusText* routine of the *httpprotocol* unit to look up standard status texts.
385+
386+You can also tell **SetStatus** to send the response at once:
387+
388+``` pascal
389+AResponse.SetStatus(404,true);
390+```
391+
392+This will set the status and call *SendResponse* to send the response. Any headers or content you have set will be sent as well.
393+
394+### Sending binary data
395+
396+An approach that seems to work to send e.g. a tiff file from the web server to the client - adapted from \$(fpcdirectory)\packages\fcl-web\examples\jsonrpc\demo1\wmdemo.pp - something like:
397+
398+``` pascal
399+AResponse.ContentStream := TMemoryStream.Create;
400+
401+try
402+ AResponse.ContentStream.LoadFromFile('/tmp/sample.tiff');
403+ AResponse.ContentType := 'image/tiff'; //or whatever MIME type you want to send
404+// to do: there is an fpweb example that gets the mime type from the file extension...
405+ AResponse.ContentLength:=AResponse.ContentStream.Size; //apparently doesn't happen automatically?
406+ AResponse.SendContent;
407+finally
408+ AResponse.ContentStream.Free;
409+end;
410+
411+Handled := true;
412+```
413+
414+### Error: Could not determine HTTP module for request
415+
416+You may have multiple modules and multiple actions. If you specify an URL with only 1 item, like:
417+
418+```html4strict
419+http://localhost/cgi-bin/somemodule
420+```
421+
422+then fpweb assumes you're specifying an action. If you don't have a default module set, you will get a 500 internal server error (Could not determine HTTP module for request)
423+
424+You can modify this behaviour to let fpweb map to a module name instead by setting the application's **PreferModuleName** property to true.
425+
426+### Error: response code 500 Internal Server error when trying to handle DELETE method
427+
428+In FPC 2.6.2 and lower, fcl-web does not accept the DELETE method and generates an error.
429+
430+### fpWeb/FCGI and Apache 2.4 (mod_proxy_fcgi mode)
431+
432+When you want to deploy FCGI application behind Apache 2.4+ reverse proxy, you need PATH_INFO variable in headers that are sent via socket to FCGI daemon.
433+
434+1, Enable following modules: setenvif_module, proxy_module and proxy_fcgi_module\
435+(on CentOS 7 modules are defined in file /etc/httpd/conf.modules.d/00-base.conf and /etc/httpd/conf.modules.d/00-proxy.conf).
436+
437+ LoadModule setenvif_module modules/mod_setenvif.so
438+ LoadModule proxy_module modules/mod_proxy.so
439+ LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
440+
441+2, Reverse proxy configuration for fpWeb/FCGI listening on port 9000.\
442+\$1 = module name\
443+\$2 = action name
444+
445+ SetEnvIf Request_URI . proxy-fcgi-pathinfo=full
446+ ProxyPassMatch "/fcgi/(.*)/(.*)" "fcgi://127.0.0.1:9000/$1/$2"
447+
448+### fpWeb/FCGI and nginx
449+
450+For correct routing, fcl-web requires the PATH_INFO variable in headers sent from nginx. For this you have to split the whole URL into FastCGI application name and the path info part whith the configuration statement *fastcgi_split_path_info*. The statement accepts a regular expression and puts the second match into *\$fastcgi_path_info* configuration variable. This variable can be passed to your application with *fastcgi_param* statement.
451+
452+The following configuration example includes a full virtual host for nginx which passes everything from *<http://myserver:8080/mycgiapp/>* to an external FastCGI application.
453+
454+ server {
455+
456+ listen 8080;
457+ listen [::]:8080;
458+
459+ server_name _;
460+
461+ root "/var/www";
462+
463+ location /mycgiap/ {
464+ # setting up PATH_INFO
465+ fastcgi_split_path_info ^(/mycgiapp/)(.*)$;
466+ fastcgi_param PATH_INFO $fastcgi_path_info;
467+
468+ # other parameters
469+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
470+ fastcgi_param QUERY_STRING $query_string;
471+ fastcgi_param REQUEST_METHOD $request_method;
472+ fastcgi_param CONTENT_TYPE $content_type;
473+ fastcgi_param CONTENT_LENGTH $content_length;
474+ fastcgi_param SCRIPT_NAME $fastcgi_script_name;
475+ fastcgi_param REQUEST_URI $request_uri;
476+ fastcgi_param DOCUMENT_URI $document_uri;
477+ fastcgi_param DOCUMENT_ROOT $document_root;
478+ fastcgi_param SERVER_PROTOCOL $server_protocol;
479+ fastcgi_param REQUEST_SCHEME $scheme;
480+ fastcgi_param HTTPS $https if_not_empty;
481+ fastcgi_param GATEWAY_INTERFACE CGI/1.1;
482+ fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
483+ fastcgi_param REMOTE_ADDR $remote_addr;
484+ fastcgi_param REMOTE_PORT $remote_port;
485+ fastcgi_param SERVER_ADDR $server_addr;
486+ fastcgi_param SERVER_PORT $server_port;
487+ fastcgi_param SERVER_NAME $server_name;
488+
489+ # pass requests to your FastCGI application
490+ fastcgi_pass 127.0.0.1:9000;
491+ }
492+ }
493+
494+## Notes
495+
496+- The cgiapp unit is deprecated, please use fpcgi as much as possible.
497+
498+- The fastcgi, custfcgi, and fpfcgi units are not supported on Darwin at this time, because it uses a different mechanism than the MSG_NOSIGNAL option for recv() to indicate that no SIGPIPE should be raised in case the connection breaks. See <http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html> for how this should be fixed.
499+
500+- If you deploy your CGI application and get errors like "Error: No action name and no default action", you should make sure there's an action assigned to the URL, or catch non-supported actions with an action marked Default. In both cases, an OnRequest event handler for the action that sets Handled:=true should be used.
501+
502+## See also
503+
504+- [[fpWeb Tutorial]]
505+- [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications
506+- [[fphttpserver]] Small stand alone Object Pascal web server component. Can be used to serve fcl-web applications.
507+- [[simpleserver]] Small stand alone server application.
508+- [[CGI Web Programming|CGI_Web_Programming#Debugging_CGI]] Information about debugging CGI applications
509+- [Part of a tutorial by Leonardo Ramé that shows how to program an fcl-web CGI application; includes example of generating JSON content](https://github.com/leonardorame/Ext4-MVC-Tutorial/wiki/Lazarus-Server)
510+- [[WCHTTPServer]] HTTP/2+HTTP1.1 server based on fcl-web
511+
512+---
513+*From the Free Pascal wiki, page "fcl-web", as it stood on 2021-09-04 after a change by Michael, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history