פרק 8: אינטרנט

[הקודם : מודולים][חזרה לתוכן העניינים][הבא : עברית בפרל]

8.1 הקדמה

בראשית היה האינטרנט. ובאינטרנט היו שרתים, והשרתים שלחו ללקוחותיהם קבצים סטטיים מהדיסק הקשיח, ויהי ערב ויהי בוקר יום אחד.
ויאמר העם: לא טוב לנו שהקבצים סטטיים. ויברא אלוהים את ה-Common Gate Interface, (או בקיצור CGI) ותשקוט הארץ שלוש שנים.
ויתאנה העם על ה-CGI, כי איטי הוא, ואוכל משאבים. ויחר אף אלוהים, ויברא את ה-ASP ואת ה-JSP ואת ה-Templates, למען ענותם ולא יוכלו דבר עוד איש אל אחיו. וישב כל תכנת בנישה הנוחה לו, ויעסיקו עצמם במלחמות דת איש עם אחיו, ולאלוהים היתה המנוחה.

8.2 פרוטוקול HTTP על קצה המזלג

קודם כל יש את השרת, שמחכה ללקוחות. בצד השני יש את הלקוח, שרוצה איזה משאב מהשרת.
הלקוח מתחבר לשרת, ושולח בקשה. הוא אומר, שלום שרת. אני לקוח מסוג (אינטרנט אקספלורר, מוזילה..) ומעוניין בקובץ (/users/list.html) הנמצא על שרת בשם (my.domain.com) והבקשה היא בשיטת (Get, Post). אני מכיר את השפות (אנגלית, עברית, ...), ותומך בעוד כמה דברים. (אתה לא חושב שאני אפרט הכל, נכון?)
השרת מקבל את הבקשה, וחושב עליה. האם מותר למשתמש הנ"ל לראות את המשאב הזה? האם המשאב הזה בכלל קיים? מאיפה אני משיג את המידע הדרוש?
אוקי. עכשיו נגיד שההרשאות בסדר, והשרת מוכן להעביר את המידע ללקוח. רגע. לאט. קודם כל צריך לשלוח ללקוח מידע על מה הוא הולך לקבל.
שלום לקוח יקר. בקשר לבקשתך, היא (מאושרת, אין לך הרשאות, לא קיימת, יש לי בעיה פנימית ואני לא יכול לספק לך את המידע כרגע). המידע הזה הוא באורך (מספר) בתים, ובקידוד (כלשהו). המידע לא השתנה מאז (אתמול, לפני שנתיים).
עכשיו השרת שולח פעמיים אנטר ("\n") כדי לומר ללקוח שפה נגמרת הכותרת ומתחיל המידע האמיתי, ושולח את המידע.
עכשיו לשיטות בקשה: יש שתי שיטות. (עיקריות) GET ו-POST.
שיטת GET, אם פעם ראית כתובת בדפדפן שלך שנראית ככה:

http://my.domain.com/some/page.html?page=1&cat=normal
זה שיטת GET. כל הפרמטרים מופיעים פשוט בכתובת, כאשר סימן שאלה מסמן את תחילת הפרמטרים, ו-& מפריד בין פרמטרים.
בשיטת POST אתה לא רואה את הפרמטרים בשורת הכתובת. הם מועברים בהחבא. היתרון הוא שזה נקי יותר, וכן אפשר להעלות קבצים בעזרת השיטה.
מבחינת המתכנת שיושב וכותב את התוכנית בשרת, רוב הכלים המודרניים מחביאים את השיטה של הבקשה, ונותנים מנשק אחיד לקבלת המידע.

8.3 המודול CGI

זוהי הצורה הוותיקה ביותר לכתיבת תוכניות על השרת. בפשטות, מדובר בקובץ הרצה רגיל, שהשרת מריץ בכל פעם שהוא מתבקש לספק את המידע. השרת מאכיל מצד אחד את הסקריפט בנתונים, (דרך הסביבה, או דרך פרמטרים, או דרך ה-stdin) ומה שהסריפט מוציא הוא שולח למשתמש.
ומכיוון שאנחנו שמנים ועצלנים, יש לנו את המודול CGI שעושה את כל העבודה השחורה בשבילנו. דוגמא לסקריפט:

#!/usr/bin/perl -w
use strict;
use CGI;
use CGI::Carp 'fatalsToBrowser';

my $q = new CGI;
my $user_name = $q->remote_user();
my $filename = $q->param('filename');
print $q->header;
print "<html><head><title>Your file</title></head><body>
Hello $user_name !<br>
You have requested file: $filename !<br>
</body></html>";

זהו סקריפט CGI פשוט, המקבל את שם המשתמש מהזיהוי משתמש הבסיסי של השרת, מקבל בתור פרמטר (ולא משנה לנו איך הפרמטר הזה התקבל) שם של קובץ, ומדפיס למשתמש את השאילתה חזרה.
שורה אחר שורה: בהתחלה יש לנו את הקידומת הרגילה של כל תוכנית פרל, עם use strict. סטנדרטי.
השורה use CGI נותנת לנו להפעיל את המודול, ואילו use CGI::Carp 'fatalsToBrowser' אומר לתוכנית, שאם יש שגיאה פאטלית, (התוכנית מסתיימת בצורה לא תקינה) אז שישלח הודעה יפה ומסודרת למשתמש, ולא משהו שהוא לא יכול לקרוא.
השורה הבאה my $q = new CGI; יוצרת אוביקט חדש. כבר פה המודול משתלט על העניינים, קורא את כל הפרמטרים הרלוונטיים, משתני הסביבה וכל מה שהוא צריך, והוא כבר מוכן לעבודה.
וכבר בשתי השורות הבאות אנחנו מפעילים אותו. הראשונה שואלת מה שם המשתמש שניגש לקובץ הזה (בהנחה שהזיהוי (Authentication) הבסיסי של השרת פועל) והשניה מקבלת פרמטר בשם filename שהמשתמש שלח עם הבקשה.
עכשיו כשאני יודע מי המשתמש ומה הוא רוצה, אני יכול לומר לו את זה. אבל קודם כל – צריך לשלוח קידומת לתשובה. ואת זה עושה השורה print $q->header;. שים לב ש-CGI לא מוציא מידע ישירות למשתמש, אלא רק מכין את התגובה הרלונטית, ונותן לך לשלוח אותה. ככה הוא עובד.
עכשיו כשגם את הקידומות שלחתי, הגיע הזמן לשלוח למשתמש את קובץ ה-HTML עצמו.
סיימנו. רק שים לב שבתשובה יש גם את השם של המשתמש וגם את הקובץ שהוא רצה. את הקובץ עצמו לא שלחנו לו. שימשיך לחלום.

8.4 טפלייטים

כמו שראית בסעיף הקודם, ה-CGI פשוט מדפיס את קובץ ה-HTML. זה בסדר כל עוד מדובר במקרה פשוט, בו כל ההדפסה המיועדת נמצאת בסוף הקובץ. אם מדובר על מקרה מסובך יותר, בו צריך לחשב דברים תוך כדי הדפסה, יכול לצאת סקריפט מכוער מאוד, כאשר קטעי HTML וקטעי קוד מעורבבים בצורה מטורפת.
כדי להפריד את הקוד מה-HTML, הומצאו הטמפלייטים. בסגנון העבודה הזה, קובץ ה-HTML נשמר בנפרד, והקוד רץ בנפרד. ובתוך קובץ ה-HTML יש סימונים מיוחדים, שמתורגמים לפני שליחת הקובץ ללקוח. הדוגמא הבאה משתמשת בחבילה הנקראת Template Toolkit:

#!/usr/bin/perl -w
use strict;
use CGI;
use CGI::Carp 'fatalsToBrowser';
use Template;

my $q = new CGI;
my $user_name = $q->remote_user();
my $filename = $q->param('filename');
print $q->header;

my $tamplate_directory = "/home/semuel/tt";
my $config = {INCLUDE_PATH => $tamplate_directory};
my $template = Template->new($config);

my $vars = {
      filename => $filename,
      user_name => $user_name,
};

# specify input filename, or file handle, text reference, etc.
my $input = 'print_that.html';

# process input template, substituting variables
$template->process($input, $vars) || die $template->error();

והקובץ print_that.html מכיל:

<html><head>
<title>Your file</title>
</head><body>
Hello [% user_name %] !<br>
You have requested file: [% filename %] !<br>
</body></html>

עכשיו נסביר. עד הקטע של הדפסת הכותרת, (print $q->header) הכל בדיוק אותו הדבר. (חוץ משורת ה-use Template)
שלושת השורות הבאות, זה סידור המנוע של הטמפלייט לקראת העבודה. קודם אני אומר לו איפה כל הטמפלייטים נמצאים, ואז מייצר אובייקט Template חדש.
אבל זה לא הכל. הטמפלייט צריך לדעת גם איפה נמצא קובץ ה-HTML, ומה להכניס לתוכו. את שם הקובץ הוא פשוט מקבל בתור פרמטר, ואת מה להכניס לתוכו הוא מקבל בצורת האש, המכיל זוגות שם וערך. ובסוף יש שורת process, שמקבלת את כל המידע ומדפיסה (בעצמה) את הפלט ללקוח.
עכשיו אם תסתכל על הקובץ print_that.html, תראה משהו דומה מאוד לסוף של ה-CGI מהסעיף הקודם. רק במקום $user_name יש [% user_name %]. המנוע של הטמפלייט בעצם מדפיס את כל הקובץ ללקוח, עד שהוא נעצר בסוגריים המרובעים הללו עם האחוזים. פה במקום להדפיס, הוא מסתכל בהאש שנתנו לו, מוצא מפתח שנקרא user_name, ומכניס במקום הסוגריים את הערך המתאים.
זו כמובן דוגמא פשוטה מאוד, המשלבת רק שני משתנים בתוך קובץ HTML. השפה של Template Toolkit היא מאוד רחבה, וכוללת משפטי תנאי, הכללת קבצים אחרים, ועוד הרבה דברים טובים. כמובן יש טמפלייט אחרים (הבדיחה טוענת שכל תכנת פרל כתב פעם מנוע טמפלייט משלו. למזלי אני ניצלתי מגורל זה. אני עצלן מדי. ☺) שכל אחד עובד קצת שונה, אבל זה כבר מחקר נפרד.
רק שים לב, שעדיין מדובר פה על תוכנית CGI. רק שבמקום להדפיס בעצמנו את ה-HTML נתנו למנוע טמפלייט לעשות זאת בשבילנו.

8.5 דפים משולבי קוד

בוא נסה לעבוד על הבעיה מכיוון אחר. במקום שתוכנית ה-CGI תדפיס את קובץ ה-HTML בחתיכות, בוא נהפוך את היוצרות. הקובץ עצמו יהיה קובץ ה-HTML, ובתוכו נשתול חתיכות קוד.
הגישה הזאת ממומשת, למשל, ע"י ASP. ברוב המקרים לא עובדים פה תחת CGI, אלא בסביבה אחרת שמראש יודעת שמדובר בקבצי HTML שבתוכם מושתל קוד. על סביבה כזאת נדבר בסעיף הבא. בנתיים, דוגמא:

<html><head>
<title>Your file</title>
</head><body>
Hello <% $r->connection->user() %> !<br>
You have requested file: <% $ARGS{filename} %> !<br>
</body></html>

זאת דוגמא הכתובה ב-Mason, שמהווה את הסביבה בה הקובץ הזה רץ. מכיוון שהסביבה מראש יודעת שמדובר בקובץ שכולל בתוכו פרל, היא מראש קוראת את כל הפרמטרים לתוך %ARGS ושולחת כותרת מתאימה. ועכשיו כמו במקרה עם הטמפלייט, הסביבה שולחת את הקובץ ללקוח, אבל את מה שתחום בתוך <% %> היא מריצה, ושולחת את התוצאה.
גם פה אפשר לעשות את כל מה שמנוע טמפלייטים עושה, רק שהסגנון הוא יותר פרלי.
הגישה הזאת מועדפת בד"כ ע"י מתכנתים שכותבים את האתר שלהם בעצמם, כיוון שהם כותבים את הלוגיקה בפרל בתוך ה-HTML. הגישה של הטמפלייט מועדפת בד"כ כאשר יש איש אחד שכותב את ה-HTML (ולא יודע פרל) ואיש אחר כותב את הפרל. ככה לזה שלא יודע פרל אין קטעי פרל שהוא לא מבין תקועים בתוך הקובץ.

8.6 ומה עוד

בסעיף השלישי הסברתי על CGI. בכל פעם שקובץ CGI מבוקש, השרת מריץ אותו ומדפיס את הפלט. הרצת תוכנית וסגירתה, כידוע, דורש הרבה ממערכת ההפעלה. היא צריכה לטעון פרל עצמו, לטעון את הקובץ, לקמפל אותו, להקצות לו זיכרון, להפעיל אותו ובסוף גם לסגור אותו. שלא לדבר אם הקובץ רוצה להתחבר למסדי נתונים ומשאבים אחרים.
מצד שני, היתרון הוא שהשרת לא צורך יותר זיכרון מהרגיל, (כי ה-CGI מקבל זיכרון משלו ממערכת ההפעלה) לא קשור רגשית אל ה-CGI, (אם קורת תקלה קולוסאלית, ה-CGI קורס באופן מרהיב, אך השרת לא אכפת לו. הוא רק משדר ללקוח שקרתה תקלה) ובכל פעם שה-CGI נסגר הוא לוקח עימו את כל הזיכרון שלו, כולל כל השגיאות והבעיות שיכלו לצוץ.
אבל בגלל שבכל זאת אנחנו רוצים לעבוד מהר יותר, המציאו את mod_perl. זה מודול של Apache, שנותן לו להריץ תוכניות פרל מבלי להצטרך לטעון אותם בכל פעם מחדש. יש אינטרפרטר אחד של פרל, שבכל פעם מקבל קובץ אחר להפעלה. הוא גם זוכר את הקבצים שהוא כבר הריץ, ככה שהשרת לא צריך לקמפל כל פעם מחדש את הסקריפט, אלא פשוט מריץ אותו שוב.
היתרונות: קודם כל מהירות. דבר שני, השרת יכול עכשיו לזכור דברים, כמו למשל אפשר לפתוח פעם אחת קישור למסד נתונים, ולהשתמש באותו הקישור לכל אורך חיי השרת.
בונוס נוסף: בגלל שפרל משולב עמוק לתוך Apache, אז יש גישה להרבה דברים שבתור CGI לא היה לנו. למשל, אפשרות לשנות את ה-url המבוקש, לפני שהשרת מתחיל ליצר את הפלט ללקוח. ועוד כל מיני דברים מתוחכמים.
החסרונות: צורך הרבה זיכרון. בגלל שפרל עכשיו הוא חלק מהשרת, השרת מחזיק אותו כל הזמן בזיכרון. מה שעושה אותו מנופח יותר, אפילו אם עכשיו הוא שולח איזה איקון ללקוח.
כמו כן, הוא זוכר דברים. שאלה אופיינית: למה השרת עושה <משהו> בפעם הראשונה שאני ניגש אליו, אבל בפעם השניה והלאה הוא עושה <משהו אחר>. התשובה היא כי יש לו משהו בזיכרון שנשאר מהפעם הראשונה.
לסיכום, שווה להשתמש בזה. יש המון יתרונות וחסרונות, אבל אם תכתוב את התוכנית כיאות, (ותתקל כמה פעם בבעיות, ותפתור אותם) ותשתמש בכמה טיפים שיחפו על החסרונות, רק תרוויח.

[הקודם : מודולים][חזרה לתוכן העניינים][הבא : עברית בפרל]

נכתב ע"י שמואל פומברג, כל הזכויות שמורות © ראה פרק 1.5 לתנאי רשיון